diff --git a/.coveragerc b/.coveragerc index 55a435a7e509..6750675da261 100644 --- a/.coveragerc +++ b/.coveragerc @@ -386,10 +386,6 @@ omit = homeassistant/components/glances/sensor.py homeassistant/components/gntp/notify.py homeassistant/components/goalfeed/* - homeassistant/components/goalzero/__init__.py - homeassistant/components/goalzero/binary_sensor.py - homeassistant/components/goalzero/sensor.py - homeassistant/components/goalzero/switch.py homeassistant/components/google/* homeassistant/components/google_cloud/tts.py homeassistant/components/google_maps/device_tracker.py diff --git a/homeassistant/components/goalzero/__init__.py b/homeassistant/components/goalzero/__init__.py index 774f1fd0e219..e03aa25f8f9e 100644 --- a/homeassistant/components/goalzero/__init__.py +++ b/homeassistant/components/goalzero/__init__.py @@ -12,6 +12,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_ATTRIBUTION, ATTR_MODEL, CONF_HOST, CONF_NAME from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers import device_registry as dr from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import ( @@ -25,6 +26,7 @@ from .const import ( DATA_KEY_API, DATA_KEY_COORDINATOR, DOMAIN, + MANUFACTURER, MIN_TIME_BETWEEN_UPDATES, ) @@ -101,8 +103,9 @@ class YetiEntity(CoordinatorEntity): def device_info(self) -> DeviceInfo: """Return the device information of the entity.""" return DeviceInfo( + connections={(dr.CONNECTION_NETWORK_MAC, self.api.sysdata["macAddress"])}, identifiers={(DOMAIN, self._server_unique_id)}, - manufacturer="Goal Zero", + manufacturer=MANUFACTURER, model=self.api.sysdata[ATTR_MODEL], name=self._name, sw_version=self.api.data["firmwareVersion"], diff --git a/homeassistant/components/goalzero/config_flow.py b/homeassistant/components/goalzero/config_flow.py index 4b02fe1e83a0..50927dd7729a 100644 --- a/homeassistant/components/goalzero/config_flow.py +++ b/homeassistant/components/goalzero/config_flow.py @@ -14,7 +14,7 @@ from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.device_registry import format_mac -from .const import DEFAULT_NAME, DOMAIN +from .const import DEFAULT_NAME, DOMAIN, MANUFACTURER _LOGGER = logging.getLogger(__name__) @@ -47,7 +47,7 @@ class GoalZeroFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Allow the user to confirm adding the device.""" if user_input is not None: return self.async_create_entry( - title="Goal Zero", + title=MANUFACTURER, data={ CONF_HOST: self.ip_address, CONF_NAME: DEFAULT_NAME, diff --git a/homeassistant/components/goalzero/const.py b/homeassistant/components/goalzero/const.py index d99cacb253ed..fef1636005d6 100644 --- a/homeassistant/components/goalzero/const.py +++ b/homeassistant/components/goalzero/const.py @@ -8,5 +8,5 @@ DATA_KEY_COORDINATOR = "coordinator" DOMAIN = "goalzero" DEFAULT_NAME = "Yeti" DATA_KEY_API = "api" - +MANUFACTURER = "Goal Zero" MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30) diff --git a/tests/components/goalzero/__init__.py b/tests/components/goalzero/__init__.py index 1b5302dbc1bb..890cd75fcd42 100644 --- a/tests/components/goalzero/__init__.py +++ b/tests/components/goalzero/__init__.py @@ -1,42 +1,95 @@ """Tests for the Goal Zero Yeti integration.""" - from unittest.mock import AsyncMock, patch from homeassistant.components.dhcp import HOSTNAME, IP_ADDRESS, MAC_ADDRESS +from homeassistant.components.goalzero import DOMAIN +from homeassistant.components.goalzero.const import DEFAULT_NAME from homeassistant.const import CONF_HOST, CONF_NAME +from homeassistant.core import HomeAssistant +from homeassistant.helpers.device_registry import format_mac +from homeassistant.setup import async_setup_component + +from tests.common import MockConfigEntry, load_fixture +from tests.test_util.aiohttp import AiohttpClientMocker HOST = "1.2.3.4" -NAME = "Yeti" +MAC = "aa:bb:cc:dd:ee:ff" CONF_DATA = { CONF_HOST: HOST, - CONF_NAME: NAME, -} - -CONF_CONFIG_FLOW = { - CONF_HOST: HOST, - CONF_NAME: NAME, + CONF_NAME: DEFAULT_NAME, } CONF_DHCP_FLOW = { - IP_ADDRESS: "1.1.1.1", - MAC_ADDRESS: "AA:BB:CC:DD:EE:FF", - HOSTNAME: "any", + IP_ADDRESS: HOST, + MAC_ADDRESS: format_mac("AA:BB:CC:DD:EE:FF"), + HOSTNAME: "yeti", } -async def _create_mocked_yeti(raise_exception=False): +def create_entry(hass: HomeAssistant): + """Add config entry in Home Assistant.""" + entry = MockConfigEntry( + domain=DOMAIN, + data=CONF_DATA, + unique_id=MAC, + ) + entry.add_to_hass(hass) + return entry + + +async def create_mocked_yeti(): + """Create mocked yeti device.""" mocked_yeti = AsyncMock() - mocked_yeti.get_state = AsyncMock() + mocked_yeti.data = {} + mocked_yeti.data["firmwareVersion"] = "1.0.0" + mocked_yeti.sysdata = {} + mocked_yeti.sysdata["model"] = "test_model" + mocked_yeti.sysdata["macAddress"] = MAC return mocked_yeti -def _patch_init_yeti(mocked_yeti): - return patch("homeassistant.components.goalzero.Yeti", return_value=mocked_yeti) - - -def _patch_config_flow_yeti(mocked_yeti): +def patch_config_flow_yeti(mocked_yeti): + """Patch Goal Zero config flow.""" return patch( "homeassistant.components.goalzero.config_flow.Yeti", return_value=mocked_yeti, ) + + +async def async_init_integration( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + skip_setup: bool = False, +) -> MockConfigEntry: + """Set up the Goal Zero integration in Home Assistant.""" + entry = create_entry(hass) + base_url = f"http://{HOST}/" + aioclient_mock.get( + f"{base_url}state", + text=load_fixture("goalzero/state_data.json"), + ) + aioclient_mock.get( + f"{base_url}sysinfo", + text=load_fixture("goalzero/info_data.json"), + ) + + if not skip_setup: + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + + return entry + + +async def async_setup_platform( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, + platform: str, +): + """Set up the platform.""" + entry = await async_init_integration(hass, aioclient_mock) + + with patch("homeassistant.components.goalzero.PLATFORMS", [platform]): + assert await async_setup_component(hass, DOMAIN, {}) + + return entry diff --git a/tests/components/goalzero/fixtures/info_data.json b/tests/components/goalzero/fixtures/info_data.json new file mode 100644 index 000000000000..6be95e6c482c --- /dev/null +++ b/tests/components/goalzero/fixtures/info_data.json @@ -0,0 +1,7 @@ +{ + "name":"yeti123456789012", + "model":"Yeti 1400", + "firmwareVersion":"1.5.7", + "macAddress":"123456789012", + "platform":"esp32" +} \ No newline at end of file diff --git a/tests/components/goalzero/fixtures/state_change.json b/tests/components/goalzero/fixtures/state_change.json new file mode 100644 index 000000000000..301a27da9541 --- /dev/null +++ b/tests/components/goalzero/fixtures/state_change.json @@ -0,0 +1,38 @@ +{ + "thingName":"yeti123456789012", + "v12PortStatus":1, + "usbPortStatus":0, + "acPortStatus":1, + "backlight":1, + "app_online":0, + "wattsIn":0.0, + "ampsIn":0.0, + "wattsOut":50.5, + "ampsOut":2.1, + "whOut":5.23, + "whStored":1330, + "volts":12.0, + "socPercent":95, + "isCharging":0, + "inputDetected":0, + "timeToEmptyFull":-1, + "temperature":25, + "wifiStrength":-62, + "ssid":"wifi", + "ipAddr":"1.2.3.4", + "timestamp":1720984, + "firmwareVersion":"1.5.7", + "version":3, + "ota":{ + "delay":0, + "status":"000-000-100_001-000-100_002-000-100_003-000-100" + }, + "notify":{ + "enabled":1048575, + "trigger":0 + }, + "foreignAcsry":{ + "model":"Yeti MPPT", + "firmwareVersion":"1.1.2" + } +} \ No newline at end of file diff --git a/tests/components/goalzero/fixtures/state_data.json b/tests/components/goalzero/fixtures/state_data.json new file mode 100644 index 000000000000..455524584f71 --- /dev/null +++ b/tests/components/goalzero/fixtures/state_data.json @@ -0,0 +1,38 @@ +{ + "thingName":"yeti123456789012", + "v12PortStatus":0, + "usbPortStatus":0, + "acPortStatus":1, + "backlight":1, + "app_online":0, + "wattsIn":0.0, + "ampsIn":0.0, + "wattsOut":50.5, + "ampsOut":2.1, + "whOut":5.23, + "whStored":1330, + "volts":12.0, + "socPercent":95, + "isCharging":0, + "inputDetected":0, + "timeToEmptyFull":-1, + "temperature":25, + "wifiStrength":-62, + "ssid":"wifi", + "ipAddr":"1.2.3.4", + "timestamp":1720984, + "firmwareVersion":"1.5.7", + "version":3, + "ota":{ + "delay":0, + "status":"000-000-100_001-000-100_002-000-100_003-000-100" + }, + "notify":{ + "enabled":1048575, + "trigger":0 + }, + "foreignAcsry":{ + "model":"Yeti MPPT", + "firmwareVersion":"1.1.2" + } +} \ No newline at end of file diff --git a/tests/components/goalzero/test_binary_sensor.py b/tests/components/goalzero/test_binary_sensor.py new file mode 100644 index 000000000000..1c1300132830 --- /dev/null +++ b/tests/components/goalzero/test_binary_sensor.py @@ -0,0 +1,36 @@ +"""Binary sensor tests for the Goalzero integration.""" +from homeassistant.components.binary_sensor import ( + DEVICE_CLASS_BATTERY_CHARGING, + DEVICE_CLASS_CONNECTIVITY, + DOMAIN, +) +from homeassistant.components.goalzero.const import DEFAULT_NAME +from homeassistant.const import ( + ATTR_DEVICE_CLASS, + DEVICE_CLASS_POWER, + STATE_OFF, + STATE_ON, +) +from homeassistant.core import HomeAssistant + +from . import async_setup_platform + +from tests.test_util.aiohttp import AiohttpClientMocker + + +async def test_binary_sensors(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker): + """Test we get sensor data.""" + await async_setup_platform(hass, aioclient_mock, DOMAIN) + + state = hass.states.get(f"binary_sensor.{DEFAULT_NAME}_backlight") + assert state.state == STATE_ON + assert state.attributes.get(ATTR_DEVICE_CLASS) is None + state = hass.states.get(f"binary_sensor.{DEFAULT_NAME}_app_online") + assert state.state == STATE_OFF + assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_CONNECTIVITY + state = hass.states.get(f"binary_sensor.{DEFAULT_NAME}_charging") + assert state.state == STATE_OFF + assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_BATTERY_CHARGING + state = hass.states.get(f"binary_sensor.{DEFAULT_NAME}_input_detected") + assert state.state == STATE_OFF + assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_POWER diff --git a/tests/components/goalzero/test_config_flow.py b/tests/components/goalzero/test_config_flow.py index 838a0f8a124b..669cace729be 100644 --- a/tests/components/goalzero/test_config_flow.py +++ b/tests/components/goalzero/test_config_flow.py @@ -3,181 +3,150 @@ from unittest.mock import patch from goalzero import exceptions -from homeassistant.components.goalzero.const import DOMAIN +from homeassistant import data_entry_flow +from homeassistant.components.goalzero.const import DEFAULT_NAME, DOMAIN, MANUFACTURER from homeassistant.config_entries import SOURCE_DHCP, SOURCE_USER -from homeassistant.data_entry_flow import ( - RESULT_TYPE_ABORT, - RESULT_TYPE_CREATE_ENTRY, - RESULT_TYPE_FORM, -) +from homeassistant.core import HomeAssistant from . import ( - CONF_CONFIG_FLOW, CONF_DATA, CONF_DHCP_FLOW, - CONF_HOST, - CONF_NAME, - NAME, - _create_mocked_yeti, - _patch_config_flow_yeti, + MAC, + create_entry, + create_mocked_yeti, + patch_config_flow_yeti, ) -from tests.common import MockConfigEntry - - -def _flow_next(hass, flow_id): - return next( - flow - for flow in hass.config_entries.flow.async_progress() - if flow["flow_id"] == flow_id - ) - def _patch_setup(): - return patch( - "homeassistant.components.goalzero.async_setup_entry", - return_value=True, - ) + return patch("homeassistant.components.goalzero.async_setup_entry") -async def test_flow_user(hass): +async def test_flow_user(hass: HomeAssistant): """Test user initialized flow.""" - mocked_yeti = await _create_mocked_yeti() - with _patch_config_flow_yeti(mocked_yeti), _patch_setup(): + mocked_yeti = await create_mocked_yeti() + with patch_config_flow_yeti(mocked_yeti), _patch_setup(): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, ) result = await hass.config_entries.flow.async_configure( result["flow_id"], - user_input=CONF_CONFIG_FLOW, + user_input=CONF_DATA, ) - assert result["type"] == RESULT_TYPE_CREATE_ENTRY - assert result["title"] == NAME + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == DEFAULT_NAME assert result["data"] == CONF_DATA + assert result["result"].unique_id == MAC -async def test_flow_user_already_configured(hass): +async def test_flow_user_already_configured(hass: HomeAssistant): """Test user initialized flow with duplicate server.""" - entry = MockConfigEntry( - domain=DOMAIN, - data={CONF_HOST: "1.2.3.4", CONF_NAME: "Yeti"}, - ) - - entry.add_to_hass(hass) - - service_info = { - "host": "1.2.3.4", - "name": "Yeti", - } + create_entry(hass) result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=service_info + DOMAIN, context={"source": SOURCE_USER}, data=CONF_DATA ) - assert result["type"] == RESULT_TYPE_ABORT + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "already_configured" -async def test_flow_user_cannot_connect(hass): +async def test_flow_user_cannot_connect(hass: HomeAssistant): """Test user initialized flow with unreachable server.""" - mocked_yeti = await _create_mocked_yeti(True) - with _patch_config_flow_yeti(mocked_yeti) as yetimock: + with patch_config_flow_yeti(await create_mocked_yeti()) as yetimock: yetimock.side_effect = exceptions.ConnectError result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=CONF_CONFIG_FLOW + DOMAIN, context={"source": SOURCE_USER}, data=CONF_DATA ) - assert result["type"] == RESULT_TYPE_FORM + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "user" - assert result["errors"] == {"base": "cannot_connect"} + assert result["errors"]["base"] == "cannot_connect" -async def test_flow_user_invalid_host(hass): +async def test_flow_user_invalid_host(hass: HomeAssistant): """Test user initialized flow with invalid server.""" - mocked_yeti = await _create_mocked_yeti(True) - with _patch_config_flow_yeti(mocked_yeti) as yetimock: + with patch_config_flow_yeti(await create_mocked_yeti()) as yetimock: yetimock.side_effect = exceptions.InvalidHost result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=CONF_CONFIG_FLOW + DOMAIN, context={"source": SOURCE_USER}, data=CONF_DATA ) - assert result["type"] == RESULT_TYPE_FORM + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "user" - assert result["errors"] == {"base": "invalid_host"} + assert result["errors"]["base"] == "invalid_host" -async def test_flow_user_unknown_error(hass): +async def test_flow_user_unknown_error(hass: HomeAssistant): """Test user initialized flow with unreachable server.""" - mocked_yeti = await _create_mocked_yeti(True) - with _patch_config_flow_yeti(mocked_yeti) as yetimock: + with patch_config_flow_yeti(await create_mocked_yeti()) as yetimock: yetimock.side_effect = Exception result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=CONF_CONFIG_FLOW + DOMAIN, context={"source": SOURCE_USER}, data=CONF_DATA ) - assert result["type"] == RESULT_TYPE_FORM + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "user" - assert result["errors"] == {"base": "unknown"} + assert result["errors"]["base"] == "unknown" -async def test_dhcp_discovery(hass): +async def test_dhcp_discovery(hass: HomeAssistant): """Test we can process the discovery from dhcp.""" - mocked_yeti = await _create_mocked_yeti() - with _patch_config_flow_yeti(mocked_yeti), _patch_setup(): + mocked_yeti = await create_mocked_yeti() + with patch_config_flow_yeti(mocked_yeti), _patch_setup(): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_DHCP}, data=CONF_DHCP_FLOW, ) - assert result["type"] == "form" + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM result = await hass.config_entries.flow.async_configure( result["flow_id"], {}, ) await hass.async_block_till_done() - assert result["type"] == RESULT_TYPE_CREATE_ENTRY - assert result["data"] == { - CONF_HOST: "1.1.1.1", - CONF_NAME: "Yeti", - } + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["title"] == MANUFACTURER + assert result["data"] == CONF_DATA + assert result["result"].unique_id == MAC result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_DHCP}, data=CONF_DHCP_FLOW, ) - assert result["type"] == RESULT_TYPE_ABORT + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "already_configured" -async def test_dhcp_discovery_failed(hass): +async def test_dhcp_discovery_failed(hass: HomeAssistant): """Test failed setup from dhcp.""" - mocked_yeti = await _create_mocked_yeti(True) - with _patch_config_flow_yeti(mocked_yeti) as yetimock: + mocked_yeti = await create_mocked_yeti() + with patch_config_flow_yeti(mocked_yeti) as yetimock: yetimock.side_effect = exceptions.ConnectError result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_DHCP}, data=CONF_DHCP_FLOW, ) - assert result["type"] == RESULT_TYPE_ABORT + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "cannot_connect" - with _patch_config_flow_yeti(mocked_yeti) as yetimock: + with patch_config_flow_yeti(mocked_yeti) as yetimock: yetimock.side_effect = exceptions.InvalidHost result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_DHCP}, data=CONF_DHCP_FLOW, ) - assert result["type"] == RESULT_TYPE_ABORT + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "invalid_host" - with _patch_config_flow_yeti(mocked_yeti) as yetimock: + with patch_config_flow_yeti(mocked_yeti) as yetimock: yetimock.side_effect = Exception result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_DHCP}, data=CONF_DHCP_FLOW, ) - assert result["type"] == RESULT_TYPE_ABORT + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "unknown" diff --git a/tests/components/goalzero/test_init.py b/tests/components/goalzero/test_init.py new file mode 100644 index 000000000000..a436b491d489 --- /dev/null +++ b/tests/components/goalzero/test_init.py @@ -0,0 +1,80 @@ +"""Test Goal Zero integration.""" +from datetime import timedelta +from unittest.mock import patch + +from goalzero import exceptions + +from homeassistant.components.goalzero.const import DEFAULT_NAME, DOMAIN, MANUFACTURER +from homeassistant.config_entries import ConfigEntryState +from homeassistant.const import STATE_ON, STATE_UNAVAILABLE +from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr +import homeassistant.util.dt as dt_util + +from . import CONF_DATA, async_init_integration, create_entry, create_mocked_yeti + +from tests.common import async_fire_time_changed +from tests.test_util.aiohttp import AiohttpClientMocker + + +async def test_setup_config_and_unload(hass: HomeAssistant): + """Test Goal Zero setup and unload.""" + entry = create_entry(hass) + mocked_yeti = await create_mocked_yeti() + with patch("homeassistant.components.goalzero.Yeti", return_value=mocked_yeti): + await hass.config_entries.async_setup(entry.entry_id) + await hass.async_block_till_done() + assert entry.state == ConfigEntryState.LOADED + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + assert entry.data == CONF_DATA + + assert await hass.config_entries.async_unload(entry.entry_id) + await hass.async_block_till_done() + + assert entry.state is ConfigEntryState.NOT_LOADED + assert not hass.data.get(DOMAIN) + + +async def test_async_setup_entry_not_ready(hass: HomeAssistant): + """Test that it throws ConfigEntryNotReady when exception occurs during setup.""" + entry = create_entry(hass) + with patch( + "homeassistant.components.goalzero.Yeti.init_connect", + side_effect=exceptions.ConnectError, + ): + await hass.config_entries.async_setup(entry.entry_id) + assert entry.state == ConfigEntryState.SETUP_RETRY + + +async def test_update_failed( + hass: HomeAssistant, + aioclient_mock: AiohttpClientMocker, +) -> None: + """Test data update failure.""" + await async_init_integration(hass, aioclient_mock) + assert hass.states.get(f"switch.{DEFAULT_NAME}_ac_port_status").state == STATE_ON + with patch( + "homeassistant.components.goalzero.Yeti.get_state", + side_effect=exceptions.ConnectError, + ) as updater: + next_update = dt_util.utcnow() + timedelta(seconds=30) + async_fire_time_changed(hass, next_update) + await hass.async_block_till_done() + updater.assert_called_once() + state = hass.states.get(f"switch.{DEFAULT_NAME}_ac_port_status") + assert state.state == STATE_UNAVAILABLE + + +async def test_device_info(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker): + """Test device info.""" + entry = await async_init_integration(hass, aioclient_mock) + device_registry = await dr.async_get_registry(hass) + + device = device_registry.async_get_device({(DOMAIN, entry.entry_id)}) + + assert device.connections == {("mac", "12:34:56:78:90:12")} + assert device.identifiers == {(DOMAIN, entry.entry_id)} + assert device.manufacturer == MANUFACTURER + assert device.model == "Yeti 1400" + assert device.name == DEFAULT_NAME + assert device.sw_version == "1.5.7" diff --git a/tests/components/goalzero/test_sensor.py b/tests/components/goalzero/test_sensor.py new file mode 100644 index 000000000000..592c43b5d431 --- /dev/null +++ b/tests/components/goalzero/test_sensor.py @@ -0,0 +1,112 @@ +"""Sensor tests for the Goalzero integration.""" +from homeassistant.components.goalzero.const import DEFAULT_NAME +from homeassistant.components.goalzero.sensor import SENSOR_TYPES +from homeassistant.components.sensor import ( + ATTR_STATE_CLASS, + DOMAIN, + STATE_CLASS_MEASUREMENT, + STATE_CLASS_TOTAL_INCREASING, +) +from homeassistant.const import ( + ATTR_DEVICE_CLASS, + ATTR_UNIT_OF_MEASUREMENT, + DEVICE_CLASS_BATTERY, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_ENERGY, + DEVICE_CLASS_POWER, + DEVICE_CLASS_SIGNAL_STRENGTH, + DEVICE_CLASS_TEMPERATURE, + DEVICE_CLASS_VOLTAGE, + ELECTRIC_CURRENT_AMPERE, + ELECTRIC_POTENTIAL_VOLT, + ENERGY_WATT_HOUR, + PERCENTAGE, + POWER_WATT, + SIGNAL_STRENGTH_DECIBELS, + TEMP_CELSIUS, + TIME_MINUTES, + TIME_SECONDS, +) +from homeassistant.core import HomeAssistant + +from . import async_setup_platform + +from tests.test_util.aiohttp import AiohttpClientMocker + + +async def test_sensors(hass: HomeAssistant, aioclient_mock: AiohttpClientMocker): + """Test we get sensor data.""" + for description in SENSOR_TYPES: + description.entity_registry_enabled_default = True + await async_setup_platform(hass, aioclient_mock, DOMAIN) + + state = hass.states.get(f"sensor.{DEFAULT_NAME}_watts_in") + assert state.state == "0.0" + assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_POWER + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == POWER_WATT + assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_MEASUREMENT + state = hass.states.get(f"sensor.{DEFAULT_NAME}_amps_in") + assert state.state == "0.0" + assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_CURRENT + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ELECTRIC_CURRENT_AMPERE + assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_MEASUREMENT + state = hass.states.get(f"sensor.{DEFAULT_NAME}_watts_out") + assert state.state == "50.5" + assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_POWER + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == POWER_WATT + assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_MEASUREMENT + state = hass.states.get(f"sensor.{DEFAULT_NAME}_amps_out") + assert state.state == "2.1" + assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_CURRENT + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ELECTRIC_CURRENT_AMPERE + assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_MEASUREMENT + state = hass.states.get(f"sensor.{DEFAULT_NAME}_wh_out") + assert state.state == "5.23" + assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_ENERGY + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_WATT_HOUR + assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_TOTAL_INCREASING + state = hass.states.get(f"sensor.{DEFAULT_NAME}_wh_stored") + assert state.state == "1330" + assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_ENERGY + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_WATT_HOUR + assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_MEASUREMENT + state = hass.states.get(f"sensor.{DEFAULT_NAME}_volts") + assert state.state == "12.0" + assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_VOLTAGE + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ELECTRIC_POTENTIAL_VOLT + assert state.attributes.get(ATTR_STATE_CLASS) is None + state = hass.states.get(f"sensor.{DEFAULT_NAME}_state_of_charge_percent") + assert state.state == "95" + assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_BATTERY + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE + assert state.attributes.get(ATTR_STATE_CLASS) is None + state = hass.states.get(f"sensor.{DEFAULT_NAME}_time_to_empty_full") + assert state.state == "-1" + assert state.attributes.get(ATTR_DEVICE_CLASS) == TIME_MINUTES + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TIME_MINUTES + assert state.attributes.get(ATTR_STATE_CLASS) is None + state = hass.states.get(f"sensor.{DEFAULT_NAME}_temperature") + assert state.state == "25" + assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TEMPERATURE + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TEMP_CELSIUS + assert state.attributes.get(ATTR_STATE_CLASS) is None + state = hass.states.get(f"sensor.{DEFAULT_NAME}_wifi_strength") + assert state.state == "-62" + assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_SIGNAL_STRENGTH + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == SIGNAL_STRENGTH_DECIBELS + assert state.attributes.get(ATTR_STATE_CLASS) is None + state = hass.states.get(f"sensor.{DEFAULT_NAME}_total_run_time") + assert state.state == "1720984" + assert state.attributes.get(ATTR_DEVICE_CLASS) is None + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TIME_SECONDS + assert state.attributes.get(ATTR_STATE_CLASS) is None + state = hass.states.get(f"sensor.{DEFAULT_NAME}_wi_fi_ssid") + assert state.state == "wifi" + assert state.attributes.get(ATTR_DEVICE_CLASS) is None + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) is None + assert state.attributes.get(ATTR_STATE_CLASS) is None + state = hass.states.get(f"sensor.{DEFAULT_NAME}_ip_address") + assert state.state == "1.2.3.4" + assert state.attributes.get(ATTR_DEVICE_CLASS) is None + assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) is None + assert state.attributes.get(ATTR_STATE_CLASS) is None diff --git a/tests/components/goalzero/test_switch.py b/tests/components/goalzero/test_switch.py new file mode 100644 index 000000000000..4c625653033f --- /dev/null +++ b/tests/components/goalzero/test_switch.py @@ -0,0 +1,51 @@ +"""Switch tests for the Goalzero integration.""" +from homeassistant.components.goalzero.const import DEFAULT_NAME +from homeassistant.components.switch import DOMAIN as DOMAIN +from homeassistant.const import ( + ATTR_ENTITY_ID, + SERVICE_TURN_OFF, + SERVICE_TURN_ON, + STATE_OFF, + STATE_ON, +) +from homeassistant.core import HomeAssistant + +from . import async_setup_platform + +from tests.common import load_fixture +from tests.test_util.aiohttp import AiohttpClientMocker + + +async def test_switches_states( + hass: HomeAssistant, aioclient_mock: AiohttpClientMocker +): + """Test we get sensor data.""" + await async_setup_platform(hass, aioclient_mock, DOMAIN) + + assert hass.states.get(f"switch.{DEFAULT_NAME}_usb_port_status").state == STATE_OFF + assert hass.states.get(f"switch.{DEFAULT_NAME}_ac_port_status").state == STATE_ON + entity_id = f"switch.{DEFAULT_NAME}_12v_port_status" + assert hass.states.get(entity_id).state == STATE_OFF + aioclient_mock.post( + "http://1.2.3.4/state", + text=load_fixture("goalzero/state_change.json"), + ) + await hass.services.async_call( + DOMAIN, + SERVICE_TURN_ON, + {ATTR_ENTITY_ID: [entity_id]}, + blocking=True, + ) + assert hass.states.get(entity_id).state == STATE_ON + aioclient_mock.clear_requests() + aioclient_mock.post( + "http://1.2.3.4/state", + text=load_fixture("goalzero/state_data.json"), + ) + await hass.services.async_call( + DOMAIN, + SERVICE_TURN_OFF, + {ATTR_ENTITY_ID: [entity_id]}, + blocking=True, + ) + assert hass.states.get(entity_id).state == STATE_OFF