Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 31 additions & 8 deletions homeassistant/components/hassio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
)


DATA_HOMEASSISTANT_VERSION = "hassio_hass_version"
DATA_INFO = "hassio_info"
DATA_HOST_INFO = "hassio_host_info"
HASSIO_UPDATE_INTERVAL = timedelta(minutes=55)

SERVICE_ADDON_START = "addon_start"
Expand Down Expand Up @@ -130,7 +131,29 @@ def get_homeassistant_version(hass):

Async friendly.
"""
return hass.data.get(DATA_HOMEASSISTANT_VERSION)
if DATA_INFO not in hass.data:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we do this? I think it would be better to make it fail as it's an unexpected situation. Integrations should depend on hassio if they want to call its methods.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know... right now it uses .get() so I assumed it is expected to be None at some point. I wanted to keep that type of "risk" out of the loop now.

We should look at the integration anyways, as some things changed at the Supervisor (new endpoint and variables) that still need to be adjusted.

return None
return hass.data[DATA_INFO].get("homeassistant")


@callback
@bind_hass
def get_info(hass):
"""Return generic information from Supervisor.

Async friendly.
"""
return hass.data.get(DATA_INFO)


@callback
@bind_hass
def get_host_info(hass):
"""Return generic host information.

Async friendly.
"""
return hass.data.get(DATA_HOST_INFO)


@callback
Expand Down Expand Up @@ -247,20 +270,20 @@ async def async_service_handler(service):
DOMAIN, service, async_service_handler, schema=settings[1]
)

async def update_homeassistant_version(now):
"""Update last available Home Assistant version."""
async def update_info_data(now):
"""Update last available supervisor information."""
try:
data = await hassio.get_homeassistant_info()
hass.data[DATA_HOMEASSISTANT_VERSION] = data["last_version"]
hass.data[DATA_INFO] = await hassio.get_info()
hass.data[DATA_HOST_INFO] = await hassio.get_host_info()
except HassioAPIError as err:
_LOGGER.warning("Can't read last version: %s", err)

hass.helpers.event.async_track_point_in_utc_time(
update_homeassistant_version, utcnow() + HASSIO_UPDATE_INTERVAL
update_info_data, utcnow() + HASSIO_UPDATE_INTERVAL
)

# Fetch last version
await update_homeassistant_version(None)
await update_info_data(None)

async def async_handle_core_service(call):
"""Service handler for handling core services."""
Expand Down
14 changes: 11 additions & 3 deletions homeassistant/components/hassio/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,20 @@ def is_connected(self):
return self.send_command("/supervisor/ping", method="get", timeout=15)

@_api_data
def get_homeassistant_info(self):
"""Return data for Home Assistant.
def get_info(self):
"""Return generic Supervisor information.

This method return a coroutine.
"""
return self.send_command("/homeassistant/info", method="get")
return self.send_command("/info", method="get")

@_api_data
def get_host_info(self):
"""Return data for Host.

This method return a coroutine.
"""
return self.send_command("/host/info", method="get")

@_api_data
def get_addon_info(self, addon):
Expand Down
23 changes: 23 additions & 0 deletions homeassistant/helpers/system_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
async def async_get_system_info(hass: HomeAssistantType) -> Dict:
"""Return info about the system."""
info_object = {
"installation_type": "Unknown",
"version": current_version,
"dev": "dev" in current_version,
"hassio": hass.components.hassio.is_hassio(),
Expand All @@ -33,4 +34,26 @@ async def async_get_system_info(hass: HomeAssistantType) -> Dict:
elif platform.system() == "Linux":
info_object["docker"] = os.path.isfile("/.dockerenv")

# Determine installation type on current data
if info_object["docker"]:
info_object["installation_type"] = "Home Assistant Core on Docker"
elif is_virtual_env():
info_object[
"installation_type"
] = "Home Assistant Core in a Python Virtual Environment"

# Enrich with Supervisor information
if hass.components.hassio.is_hassio():
info = hass.components.hassio.get_info()
host = hass.components.hassio.get_host_info()

info_object["supervisor"] = info.get("supervisor")
info_object["host_os"] = host.get("operating_system")
info_object["chassis"] = host.get("chassis")

if info.get("hassos") is not None:
info_object["installation_type"] = "Home Assistant"
else:
info_object["installation_type"] = "Home Assistant Supervised"

return info_object
5 changes: 2 additions & 3 deletions tests/components/hassio/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def hassio_env():
"homeassistant.components.hassio.HassIO.is_connected",
return_value={"result": "ok", "data": {}},
), patch.dict(os.environ, {"HASSIO_TOKEN": "123456"}), patch(
"homeassistant.components.hassio.HassIO.get_homeassistant_info",
"homeassistant.components.hassio.HassIO.get_info",
Mock(side_effect=HassioAPIError()),
):
yield
Expand All @@ -35,8 +35,7 @@ def hassio_stubs(hassio_env, hass, hass_client, aioclient_mock):
"homeassistant.components.hassio.HassIO.update_hass_timezone",
return_value={"result": "ok"},
), patch(
"homeassistant.components.hassio.HassIO.get_homeassistant_info",
side_effect=HassioAPIError(),
"homeassistant.components.hassio.HassIO.get_info", side_effect=HassioAPIError(),
):
hass.state = CoreState.starting
hass.loop.run_until_complete(async_setup_component(hass, "hassio", {}))
Expand Down
2 changes: 1 addition & 1 deletion tests/components/hassio/test_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ async def test_hassio_discovery_startup_done(hass, aioclient_mock, hassio_client
"homeassistant.components.hassio.HassIO.update_hass_api",
return_value={"result": "ok"},
), patch(
"homeassistant.components.hassio.HassIO.get_homeassistant_info",
"homeassistant.components.hassio.HassIO.get_info",
Mock(side_effect=HassioAPIError()),
), patch(
"homeassistant.components.mqtt.config_flow.FlowHandler.async_step_hassio",
Expand Down
56 changes: 47 additions & 9 deletions tests/components/hassio/test_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,64 @@ async def test_api_ping_exeption(hassio_handler, aioclient_mock):
assert aioclient_mock.call_count == 1


async def test_api_homeassistant_info(hassio_handler, aioclient_mock):
"""Test setup with API Home Assistant info."""
async def test_api_info(hassio_handler, aioclient_mock):
"""Test setup with API generic info."""
aioclient_mock.get(
"http://127.0.0.1/homeassistant/info",
json={"result": "ok", "data": {"last_version": "10.0"}},
"http://127.0.0.1/info",
json={
"result": "ok",
"data": {"supervisor": "222", "homeassistant": "0.110.0", "hassos": None},
},
)

data = await hassio_handler.get_info()
assert aioclient_mock.call_count == 1
assert data["hassos"] is None
assert data["homeassistant"] == "0.110.0"
assert data["supervisor"] == "222"


async def test_api_info_error(hassio_handler, aioclient_mock):
"""Test setup with API Home Assistant info error."""
aioclient_mock.get(
"http://127.0.0.1/info", json={"result": "error", "message": None}
)

with pytest.raises(HassioAPIError):
await hassio_handler.get_info()

assert aioclient_mock.call_count == 1


async def test_api_host_info(hassio_handler, aioclient_mock):
"""Test setup with API Host info."""
aioclient_mock.get(
"http://127.0.0.1/host/info",
json={
"result": "ok",
"data": {
"chassis": "vm",
"operating_system": "Debian GNU/Linux 10 (buster)",
"kernel": "4.19.0-6-amd64",
},
},
)

data = await hassio_handler.get_homeassistant_info()
data = await hassio_handler.get_host_info()
assert aioclient_mock.call_count == 1
assert data["last_version"] == "10.0"
assert data["chassis"] == "vm"
assert data["kernel"] == "4.19.0-6-amd64"
assert data["operating_system"] == "Debian GNU/Linux 10 (buster)"


async def test_api_homeassistant_info_error(hassio_handler, aioclient_mock):
async def test_api_host_info_error(hassio_handler, aioclient_mock):
"""Test setup with API Home Assistant info error."""
aioclient_mock.get(
"http://127.0.0.1/homeassistant/info", json={"result": "error", "message": None}
"http://127.0.0.1/host/info", json={"result": "error", "message": None}
)

with pytest.raises(HassioAPIError):
await hassio_handler.get_homeassistant_info()
await hassio_handler.get_host_info()

assert aioclient_mock.call_count == 1

Expand Down
37 changes: 27 additions & 10 deletions tests/components/hassio/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,25 @@ def mock_all(aioclient_mock):
aioclient_mock.get("http://127.0.0.1/supervisor/ping", json={"result": "ok"})
aioclient_mock.post("http://127.0.0.1/supervisor/options", json={"result": "ok"})
aioclient_mock.get(
"http://127.0.0.1/homeassistant/info",
json={"result": "ok", "data": {"last_version": "10.0"}},
"http://127.0.0.1/info",
json={
"result": "ok",
"data": {"supervisor": "222", "homeassistant": "0.110.0", "hassos": None},
},
)
aioclient_mock.get(
"http://127.0.0.1/host/info",
json={
"result": "ok",
"data": {
"result": "ok",
"data": {
"chassis": "vm",
"operating_system": "Debian GNU/Linux 10 (buster)",
"kernel": "4.19.0-6-amd64",
},
},
},
)
aioclient_mock.get(
"http://127.0.0.1/ingress/panels", json={"result": "ok", "data": {"panels": {}}}
Expand All @@ -34,8 +51,8 @@ async def test_setup_api_ping(hass, aioclient_mock):
result = await async_setup_component(hass, "hassio", {})
assert result

assert aioclient_mock.call_count == 5
assert hass.components.hassio.get_homeassistant_version() == "10.0"
assert aioclient_mock.call_count == 6
assert hass.components.hassio.get_homeassistant_version() == "0.110.0"
assert hass.components.hassio.is_hassio()


Expand Down Expand Up @@ -73,7 +90,7 @@ async def test_setup_api_push_api_data(hass, aioclient_mock):
)
assert result

assert aioclient_mock.call_count == 5
assert aioclient_mock.call_count == 6
assert not aioclient_mock.mock_calls[1][2]["ssl"]
assert aioclient_mock.mock_calls[1][2]["port"] == 9999
assert aioclient_mock.mock_calls[1][2]["watchdog"]
Expand All @@ -89,7 +106,7 @@ async def test_setup_api_push_api_data_server_host(hass, aioclient_mock):
)
assert result

assert aioclient_mock.call_count == 5
assert aioclient_mock.call_count == 6
assert not aioclient_mock.mock_calls[1][2]["ssl"]
assert aioclient_mock.mock_calls[1][2]["port"] == 9999
assert not aioclient_mock.mock_calls[1][2]["watchdog"]
Expand All @@ -101,7 +118,7 @@ async def test_setup_api_push_api_data_default(hass, aioclient_mock, hass_storag
result = await async_setup_component(hass, "hassio", {"http": {}, "hassio": {}})
assert result

assert aioclient_mock.call_count == 5
assert aioclient_mock.call_count == 6
assert not aioclient_mock.mock_calls[1][2]["ssl"]
assert aioclient_mock.mock_calls[1][2]["port"] == 8123
refresh_token = aioclient_mock.mock_calls[1][2]["refresh_token"]
Expand Down Expand Up @@ -148,7 +165,7 @@ async def test_setup_api_existing_hassio_user(hass, aioclient_mock, hass_storage
result = await async_setup_component(hass, "hassio", {"http": {}, "hassio": {}})
assert result

assert aioclient_mock.call_count == 5
assert aioclient_mock.call_count == 6
assert not aioclient_mock.mock_calls[1][2]["ssl"]
assert aioclient_mock.mock_calls[1][2]["port"] == 8123
assert aioclient_mock.mock_calls[1][2]["refresh_token"] == token.token
Expand All @@ -162,7 +179,7 @@ async def test_setup_core_push_timezone(hass, aioclient_mock):
result = await async_setup_component(hass, "hassio", {"hassio": {}})
assert result

assert aioclient_mock.call_count == 5
assert aioclient_mock.call_count == 6
assert aioclient_mock.mock_calls[2][2]["timezone"] == "testzone"

await hass.config.async_update(time_zone="America/New_York")
Expand All @@ -178,7 +195,7 @@ async def test_setup_hassio_no_additional_data(hass, aioclient_mock):
result = await async_setup_component(hass, "hassio", {"hassio": {}})
assert result

assert aioclient_mock.call_count == 5
assert aioclient_mock.call_count == 6
assert aioclient_mock.mock_calls[-1][3]["X-Hassio-Key"] == "123456"


Expand Down
7 changes: 6 additions & 1 deletion tests/components/updater/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,12 @@ async def test_new_version_shows_entity_after_hour_hassio(
"""Test if binary sensor gets updated if new version is available / Hass.io."""
mock_get_uuid.return_value = MOCK_HUUID
mock_component(hass, "hassio")
hass.data["hassio_hass_version"] = "999.0"
hass.data["hassio_info"] = {"hassos": None, "homeassistant": "999.0"}
hass.data["hassio_host"] = {
"supervisor": "222",
"chassis": "vm",
"operating_system": "HassOS 4.6",
}

assert await async_setup_component(hass, updater.DOMAIN, {updater.DOMAIN: {}})

Expand Down