diff --git a/haaska/.gitignore b/haaska/.gitignore
new file mode 100644
index 0000000..5570802
--- /dev/null
+++ b/haaska/.gitignore
@@ -0,0 +1,104 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+.noseids
+# Distribution / packaging
+.vscode/
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+.hypothesis/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# dotenv
+.env
+
+# virtualenv
+.venv
+venv/
+ENV/
+
+# Spyder project settings
+.spyderproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+build
+.DS_Store
+haaska.zip
+/.vs
+/.idea/
diff --git a/haaska/CHANGELOG.md b/haaska/CHANGELOG.md
new file mode 100644
index 0000000..2c96823
--- /dev/null
+++ b/haaska/CHANGELOG.md
@@ -0,0 +1,61 @@
+# haaska Changelog
+
+## [0.5] - 2018-09-18
+###
+- Breaking Change: Remove support for the legacy_auth provider in homeassistant
+- Implemented authentication using Long-Lived Access Tokens
+
+## [0.4] - 2018-01-24
+###
+- Changed code to work with Hass 0.62, please note this skill will break on any version older than this.
+
+## [0.3.1] - 2017-06-24
+### Changed
+- Hotfix for a logic error in exposed/hidden entities.
+- Fixed a few formatting and link errors in the changelog.
+
+## [0.3] - 2017-06-24
+### Added
+- There's now a `discover` target in the `Makefile`, which will send a discovery
+ request to your running haaska instance using the AWS CLI and print the
+ results using `jq`. This is helpful for debugging configuration changes.
+- Color temperature can now be incremented and decremented (*"Alexa, make lamp
+ cooler"*, *"Alexa, make lamp warmer"*).
+- `input_slider`, `automation`, and `alert` entities are now supported.
+- There's now a configuration option to hide Home Assistant entities by default.
+
+### Changed
+- haaska will no longer wait for a response from a POST to Home Assistant. This
+ reduces the delay between issuing a command and getting a confirmation from
+ Alexa on some devices.
+- haaska will now accept numbers encoded as strings for thermostat commands.
+ This makes haaska a bit more robust, since these commands seem to have
+ inconsistent types at times.
+
+## [0.2] - 2017-05-07
+### Added
+- Support for controlling the color (*"Alexa, turn kitchen green"*) and color
+ temperature (*"Alexa, set lamp to cool white"*) of lights.
+- Support for controlling fans.
+
+### Changed
+- The format of `config.json` has changed, though old formats are still
+ supported. To migrate to the new format, run `make modernize_config`.
+- Instead of the hardcoded "Group" and "Scene" suffixes on entities in those
+ domains, the suffix is now configurable on a per-domain basis using the
+ `entity_suffixes` key in the configuration file.
+- Entities hidden in Home Assistant (via the `hidden` attribute) are now hidden
+ from haaska.
+- Improved logging, and added a way to increase verbosity for
+ debugging. Set the `debug` key in the configuration to `true`
+ to enable more verbose logging to CloudWatch.
+
+## [0.1] - 2017-03-19
+
+First tagged release.
+
+[unreleased]: https://github.com/auchter/haaska/tree/dev
+[0.3.1]: https://github.com/auchter/haaska/tree/0.3.1
+[0.3]: https://github.com/auchter/haaska/tree/0.3
+[0.2]: https://github.com/auchter/haaska/tree/0.2
+[0.1]: https://github.com/auchter/haaska/tree/0.1
diff --git a/haaska/Dockerfile b/haaska/Dockerfile
new file mode 100644
index 0000000..466d9a8
--- /dev/null
+++ b/haaska/Dockerfile
@@ -0,0 +1,17 @@
+FROM python:3.10
+
+RUN \
+ apt-get update && \
+ apt-get install -y jq zip && \
+ pip install awscli && \
+ apt-get clean && \
+ cd /var/lib/apt/lists && rm -fr *Release* *Sources* *Packages* && \
+ truncate -s 0 /var/log/*log
+
+RUN mkdir -p /usr/src/app
+
+WORKDIR /usr/src/app
+
+COPY . /usr/src/app
+
+CMD ["make"]
diff --git a/haaska/LICENSE b/haaska/LICENSE
new file mode 100644
index 0000000..063efb3
--- /dev/null
+++ b/haaska/LICENSE
@@ -0,0 +1,23 @@
+MIT License
+
+Copyright (c) 2015 Michael Auchter
+Copyright (c) 2018 Phil Frost (bitglue)
+Copyright (c) 2018 Mike Grant and contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/haaska/Makefile b/haaska/Makefile
new file mode 100644
index 0000000..8141ab9
--- /dev/null
+++ b/haaska/Makefile
@@ -0,0 +1,75 @@
+
+SHELL := /bin/bash
+
+# Function name in AWS Lambda:
+FUNCTION_NAME=haaska
+
+BUILD_DIR=build
+
+PIPVERSIONEQ9 := $(shell expr `pip3 -V | cut -d ' ' -f 2` \= 9.0.1)
+
+ifneq (,$(wildcard /etc/debian_version))
+ ifeq "$(PIPVERSIONEQ9)" "1"
+ PIP_VER=3
+ PIP_EXTRA = --system
+ endif
+else
+ PIP_VER =
+ PIP_EXTRA =
+endif
+
+haaska.zip: haaska.py config/*
+ mkdir -p $(BUILD_DIR)
+ cp $^ $(BUILD_DIR)
+ pip$(PIP_VER) install $(PIP_EXTRA) -t $(BUILD_DIR) requests
+ chmod 755 $(BUILD_DIR)/haaska.py
+ cd $(BUILD_DIR); zip ../$@ -r *
+
+.PHONY: deploy
+deploy: haaska.zip
+ aws lambda update-function-configuration \
+ --function-name $(FUNCTION_NAME) \
+ --handler haaska.event_handler
+ aws lambda update-function-code \
+ --function-name $(FUNCTION_NAME) \
+ --zip-file fileb://$<
+
+DISCOVERY_PAYLOAD:=' \
+{ \
+ "directive": { \
+ "header": { \
+ "namespace": "Alexa.Discovery", \
+ "name": "Discover", \
+ "payloadVersion": "3", \
+ "messageId": "1bd5d003-31b9-476f-ad03-71d471922820" \
+ }, \
+ "payload": { \
+ "scope": { \
+ "type": "BearerToken", \
+ "token": "access-token-from-skill" \
+ } \
+ } \
+ } \
+}'
+
+.PHONY: discover
+discover:
+ @aws lambda invoke \
+ --function-name $(FUNCTION_NAME) \
+ --payload ${DISCOVERY_PAYLOAD} \
+ /dev/fd/3 3>&1 >/dev/null | jq '.'
+
+
+.PHONY: clean
+clean:
+ rm -rf $(BUILD_DIR) haaska.zip
+
+.PHONY: sample_config
+sample_config:
+ python -c 'from haaska import Configuration; print(Configuration().dump())' > config/config.json.sample
+
+.PHONY: modernize_config
+modernize_config: config/config.json
+ @python -c 'from haaska import Configuration; print(Configuration("config/config.json").dump())' > config/config.json.modernized
+ @echo Generated config/config.json.modernized from your existing config/config.json
+ @echo Inspect that file and replace config/config.json with it to update your configuration
diff --git a/haaska/README.md b/haaska/README.md
new file mode 100644
index 0000000..fb39679
--- /dev/null
+++ b/haaska/README.md
@@ -0,0 +1,33 @@
+# haaska: Home Assistant Alexa Skill Adapter
+
+[![Main](https://github.com/mike-grant/haaska/actions/workflows/main.yml/badge.svg)](https://github.com/mike-grant/haaska/actions/workflows/main.yml)
+
+---
+
+haaska implements a bridge between the [Home Assistant Smart Home API](https://www.home-assistant.io/components/alexa/#smart-home) and the [Alexa Smart Home Skill API](https://developer.amazon.com/alexa/smart-home) from Amazon.
+
+This provides voice control for a connected home managed by Home Assistant, through any Alexa-enabled device.
+
+### Getting Started
+To get started, head over to the [haaska Wiki](https://github.com/mike-grant/haaska/wiki).
+
+### Development
+
+Run tests
+
+```
+python -m pytest test.py
+```
+
+### Thanks and Acknowledgement
+
+Thanks to [@auchter](https://github.com/auchter) for creating the original haaska.
+
+Thanks to [@bitglue](https://github.com/bitglue) for his work in getting the Smart Home API exposed via HTTP, making this slimmed down version possible.
+
+This fork of haaska was created by [@mike-grant](https://github.com/mike-grant).
+
+Documentation and additional maintenance is done by [@anthonylavado](https://github.com/anthonylavado), and contributors like you.
+
+### License
+haaska is provided under the [MIT License](LICENSE).
diff --git a/haaska/config/config.json.sample b/haaska/config/config.json.sample
new file mode 100644
index 0000000..2becfc1
--- /dev/null
+++ b/haaska/config/config.json.sample
@@ -0,0 +1,7 @@
+{
+ "url": "http://localhost:8123/api",
+ "bearer_token": "",
+ "debug": false,
+ "ssl_verify": true,
+ "ssl_client": []
+}
diff --git a/haaska/haaska.py b/haaska/haaska.py
new file mode 100644
index 0000000..b18d1a2
--- /dev/null
+++ b/haaska/haaska.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+# Copyright (c) 2015 Michael Auchter
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import os
+import json
+import logging
+import requests
+
+logger = logging.getLogger()
+
+
+class HomeAssistant(object):
+ def __init__(self, config):
+ self.config = config
+
+ self.session = requests.Session()
+ self.session.headers = {
+ 'Authorization': f'Bearer {config.bearer_token}',
+ 'content-type': 'application/json',
+ 'User-Agent': self.get_user_agent()
+ }
+ self.session.verify = config.ssl_verify
+ self.session.cert = config.ssl_client
+
+ def build_url(self, endpoint):
+ return f'{self.config.url}/api/{endpoint}'
+
+ def get_user_agent(self):
+ library = "Home Assistant Alexa Smart Home Skill"
+ aws_region = os.environ.get("AWS_DEFAULT_REGION")
+ logger.debug(
+ f'AWS_DEFAULT_REGION is { aws_region}')
+ default_user_agent = requests.utils.default_user_agent()
+ return f"{library} - {aws_region} - {default_user_agent}"
+
+ def get(self, endpoint):
+ r = self.session.get(self.build_url(endpoint))
+ r.raise_for_status()
+ return r.json()
+
+ def post(self, endpoint, data, wait=False):
+ read_timeout = None if wait else 0.01
+ try:
+ logger.debug(f'calling {endpoint} with {data}')
+ r = self.session.post(self.build_url(endpoint),
+ data=json.dumps(data),
+ timeout=(None, read_timeout))
+ r.raise_for_status()
+ return r.json()
+ except requests.exceptions.ReadTimeout:
+ # Allow response timeouts after request was sent
+ logger.debug(
+ f'request for {endpoint} sent without waiting for response')
+ return None
+
+
+class Configuration(object):
+ def __init__(self, filename=None, opts_dict=None):
+ self._json = {}
+ if filename is not None:
+ with open(filename) as f:
+ self._json = json.load(f)
+
+ if opts_dict is not None:
+ self._json = opts_dict
+
+ self.url = os.environ.get("HA_URL")
+ self.bearer_token = os.environ.get("HA_TOKEN")
+ self.ssl_verify = os.environ.get("SSL_VERIFY", default=False)
+ self.ssl_client = os.environ.get("SSL_CLIENT", default='')
+ self.debug = os.environ.get("DEBUG", default=False)
+
+ logger.debug(
+ f'HA_URL is { self.url}')
+
+ if self.debug:
+ logger.setLevel(logging.DEBUG)
+
+ def get(self, keys, default=None):
+ for key in keys:
+ if key in self._json:
+ return self._json[key]
+ return default
+
+ def get_url(self, url):
+ """Returns Home Assistant base url without '/api' or trailing slash"""
+ if not url:
+ raise ValueError('Property "url" is missing in config')
+
+ return url.replace("/api", "").rstrip("/")
+
+
+def event_handler(event, context):
+ config = Configuration('config.json')
+ if config.debug:
+ logger.setLevel(logging.DEBUG)
+ ha = HomeAssistant(config)
+
+ return ha.post('alexa/smart_home', event, wait=True)
diff --git a/haaska/images/108x108.png b/haaska/images/108x108.png
new file mode 100644
index 0000000..f1a56e1
Binary files /dev/null and b/haaska/images/108x108.png differ
diff --git a/haaska/images/512x512.png b/haaska/images/512x512.png
new file mode 100644
index 0000000..e48e6ea
Binary files /dev/null and b/haaska/images/512x512.png differ
diff --git a/haaska/requirements-test.txt b/haaska/requirements-test.txt
new file mode 100644
index 0000000..bb2d7d8
--- /dev/null
+++ b/haaska/requirements-test.txt
@@ -0,0 +1,3 @@
+-r requirements.txt
+pytest==7.1.2
+flake8==4.0.1
diff --git a/haaska/requirements.txt b/haaska/requirements.txt
new file mode 100644
index 0000000..566083c
--- /dev/null
+++ b/haaska/requirements.txt
@@ -0,0 +1 @@
+requests==2.22.0
diff --git a/haaska/test.py b/haaska/test.py
new file mode 100644
index 0000000..c2d81d5
--- /dev/null
+++ b/haaska/test.py
@@ -0,0 +1,48 @@
+import os
+import pytest
+
+from haaska import HomeAssistant, Configuration
+
+
+@pytest.fixture
+def configuration():
+ return Configuration(opts_dict={
+ "url": "http://localhost:8123",
+ "bearer_token": "",
+ "debug": False,
+ "ssl_verify": True,
+ "ssl_client": []
+ })
+
+
+@pytest.fixture
+def home_assistant(configuration):
+ return HomeAssistant(configuration)
+
+
+def test_ha_build_url(home_assistant):
+ url = home_assistant.build_url("test")
+ assert url == "http://localhost:8123/api/test"
+
+
+def test_get_user_agent(home_assistant):
+ os.environ["AWS_DEFAULT_REGION"] = "test"
+ user_agent = home_assistant.get_user_agent()
+ assert user_agent.startswith("Home Assistant Alexa Smart Home Skill - test - python-requests/")
+
+
+def test_config_get(configuration):
+ assert configuration.get(["debug"]) is False
+ assert configuration.get(["test"]) is None
+ assert configuration.get(["test"], default="default") == "default"
+
+
+def test_config_get_url(configuration):
+ test_urls = [
+ "http://hass.example.com:8123",
+ "http://hass.example.app"
+ ]
+ for expected_url in test_urls:
+ assert configuration.get_url(expected_url + "/") == expected_url
+ assert configuration.get_url(expected_url + "/api") == expected_url
+ assert configuration.get_url(expected_url + "/api/") == expected_url