Exemplo n.º 1
0
def _login_and_fetch_site_info(
    power_wall: Powerwall, password: str
) -> tuple[SiteInfo, str]:
    """Login to the powerwall and fetch the base info."""
    if password is not None:
        power_wall.login(password)
    return power_wall.get_site_info(), power_wall.get_gateway_din()
Exemplo n.º 2
0
def _login_and_fetch_base_info(
    power_wall: Powerwall, host: str, password: str
) -> PowerwallBaseInfo:
    """Login to the powerwall and fetch the base info."""
    if password is not None:
        power_wall.login(password)
    return call_base_info(power_wall, host)
Exemplo n.º 3
0
 def _recreate_powerwall_login(self) -> None:
     """Recreate the login on auth failure."""
     http_session = self.runtime_data[POWERWALL_HTTP_SESSION]
     http_session.close()
     http_session = requests.Session()
     self.runtime_data[POWERWALL_HTTP_SESSION] = http_session
     self.power_wall = Powerwall(self.ip_address, http_session=http_session)
     self.power_wall.login(self.password or "")
Exemplo n.º 4
0
def _fetch_powerwall_data(power_wall: Powerwall) -> PowerwallData:
    """Process and update powerwall data."""
    return PowerwallData(
        charge=power_wall.get_charge(),
        site_master=power_wall.get_sitemaster(),
        meters=power_wall.get_meters(),
        grid_services_active=power_wall.is_grid_services_active(),
        grid_status=power_wall.get_grid_status(),
    )
Exemplo n.º 5
0
 def _recreate_powerwall_login():
     nonlocal http_session
     nonlocal power_wall
     http_session.close()
     http_session = requests.Session()
     power_wall = Powerwall(ip_address, http_session=http_session)
     runtime_data[POWERWALL_OBJECT] = power_wall
     runtime_data[POWERWALL_HTTP_SESSION] = http_session
     power_wall.login("", password)
Exemplo n.º 6
0
 def test_detect_and_pin_version(self):
     add(
         Response(responses.GET,
                  url=f"{ENDPOINT}status",
                  json=STATUS_RESPONSE))
     vers = version.LooseVersion("1.50.1")
     pw = Powerwall(ENDPOINT)
     self.assertEqual(pw.detect_and_pin_version(), vers)
     self.assertEqual(pw._pin_version, vers)
Exemplo n.º 7
0
    def test_endpoint_setup(self):
        test_endpoint_1 = "1.1.1.1"
        pw = Powerwall(test_endpoint_1)
        self.assertEqual(pw._endpoint, f"https://{test_endpoint_1}/api/")

        test_endpoint_2 = "http://1.1.1.1"
        pw = Powerwall(test_endpoint_2)
        self.assertEqual(pw._endpoint, f"https://1.1.1.1/api/")

        test_endpoint_3 = "https://1.1.1.1/api/"
        pw = Powerwall(test_endpoint_3)
        self.assertEqual(pw._endpoint, test_endpoint_3)
Exemplo n.º 8
0
def call_base_info(power_wall: Powerwall, host: str) -> PowerwallBaseInfo:
    """Return PowerwallBaseInfo for the device."""
    # Make sure the serial numbers always have the same order
    gateway_din = None
    with contextlib.suppress(AssertionError, PowerwallError):
        gateway_din = power_wall.get_gateway_din().upper()
    return PowerwallBaseInfo(
        gateway_din=gateway_din,
        site_info=power_wall.get_site_info(),
        status=power_wall.get_status(),
        device_type=power_wall.get_device_type(),
        serial_numbers=sorted(power_wall.get_serial_numbers()),
        url=f"https://{host}",
    )
def main():
    pp = pprint.PrettyPrinter(indent=2)

    creds = get_login_credentials()

    power_wall = Powerwall(BACKUP_GW_ADDR)
    try:
        power_wall.detect_and_pin_version()
    except PowerwallUnreachableError as e:
        print(e)
        return

    print("Detected and pinned version: {}".format(
        power_wall.get_pinned_version()))
Exemplo n.º 10
0
def _mock_powerwall_return_value(
    site_info=None,
    charge=None,
    sitemaster=None,
    meters=None,
    grid_services_active=None,
    grid_status=None,
    status=None,
    device_type=None,
    serial_numbers=None,
    backup_reserve_percentage=None,
):
    powerwall_mock = MagicMock(Powerwall("1.2.3.4"))
    powerwall_mock.get_site_info = Mock(return_value=site_info)
    powerwall_mock.get_charge = Mock(return_value=charge)
    powerwall_mock.get_sitemaster = Mock(return_value=sitemaster)
    powerwall_mock.get_meters = Mock(return_value=meters)
    powerwall_mock.get_grid_status = Mock(return_value=grid_status)
    powerwall_mock.get_status = Mock(return_value=status)
    powerwall_mock.get_device_type = Mock(return_value=device_type)
    powerwall_mock.get_serial_numbers = Mock(return_value=serial_numbers)
    powerwall_mock.get_backup_reserve_percentage = Mock(
        return_value=backup_reserve_percentage)
    powerwall_mock.is_grid_services_active = Mock(
        return_value=grid_services_active)

    return powerwall_mock
Exemplo n.º 11
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Tesla Powerwall from a config entry."""
    http_session = requests.Session()
    ip_address = entry.data[CONF_IP_ADDRESS]

    password = entry.data.get(CONF_PASSWORD)
    power_wall = Powerwall(ip_address, http_session=http_session)
    try:
        base_info = await hass.async_add_executor_job(
            _login_and_fetch_base_info, power_wall, ip_address, password)
    except PowerwallUnreachableError as err:
        http_session.close()
        raise ConfigEntryNotReady from err
    except MissingAttributeError as err:
        http_session.close()
        # The error might include some important information about what exactly changed.
        _LOGGER.error("The powerwall api has changed: %s", str(err))
        persistent_notification.async_create(hass, API_CHANGED_ERROR_BODY,
                                             API_CHANGED_TITLE)
        return False
    except AccessDeniedError as err:
        _LOGGER.debug("Authentication failed", exc_info=err)
        http_session.close()
        raise ConfigEntryAuthFailed from err
    except APIError as err:
        http_session.close()
        raise ConfigEntryNotReady from err

    gateway_din = base_info.gateway_din
    if gateway_din and entry.unique_id is not None and is_ip_address(
            entry.unique_id):
        hass.config_entries.async_update_entry(entry, unique_id=gateway_din)

    runtime_data = PowerwallRuntimeData(
        api_changed=False,
        base_info=base_info,
        http_session=http_session,
        coordinator=None,
    )

    manager = PowerwallDataManager(hass, power_wall, ip_address, password,
                                   runtime_data)

    coordinator = DataUpdateCoordinator(
        hass,
        _LOGGER,
        name="Powerwall site",
        update_method=manager.async_update_data,
        update_interval=timedelta(seconds=UPDATE_INTERVAL),
    )

    await coordinator.async_config_entry_first_refresh()

    runtime_data[POWERWALL_COORDINATOR] = coordinator

    hass.data.setdefault(DOMAIN, {})[entry.entry_id] = runtime_data

    await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

    return True
Exemplo n.º 12
0
async def _mock_powerwall_site_name(hass, site_name):
    powerwall_mock = MagicMock(Powerwall("1.2.3.4"))

    site_info_resp = SiteInfoResponse(await _async_load_json_fixture(
        hass, "site_info.json"))
    site_info_resp.site_name = site_name
    powerwall_mock.get_site_info = Mock(return_value=site_info_resp)

    return powerwall_mock
Exemplo n.º 13
0
async def _mock_powerwall_site_name(opp, site_name):
    powerwall_mock = MagicMock(Powerwall("1.2.3.4"))

    site_info_resp = SiteInfo(await
                              _async_load_json_fixture(opp, "site_info.json"))
    # Sets site_info_resp.site_name to return site_name
    site_info_resp.response["site_name"] = site_name
    powerwall_mock.get_site_info = Mock(return_value=site_info_resp)

    return powerwall_mock
Exemplo n.º 14
0
async def _mock_powerwall_site_name(hass, site_name):
    powerwall_mock = MagicMock(Powerwall("1.2.3.4"))

    site_info_resp = SiteInfo(await
                              _async_load_json_fixture(hass, "site_info.json"))
    # Sets site_info_resp.site_name to return site_name
    site_info_resp.response["site_name"] = site_name
    powerwall_mock.get_site_info = Mock(return_value=site_info_resp)
    powerwall_mock.get_gateway_din = Mock(return_value=MOCK_GATEWAY_DIN)

    return powerwall_mock
Exemplo n.º 15
0
    def test_pins_version_on_creation(self):
        pw = Powerwall(ENDPOINT, pin_version="1.49.0")
        self.assertEqual(pw.get_pinned_version(),
                         version.LooseVersion("1.49.0"))

        pw = Powerwall(ENDPOINT, pin_version=version.LooseVersion("1.49.0"))
        self.assertEqual(pw.get_pinned_version(),
                         version.LooseVersion("1.49.0"))
Exemplo n.º 16
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
    """Set up Tesla Powerwall from a config entry."""

    entry_id = entry.entry_id

    hass.data[DOMAIN].setdefault(entry_id, {})
    http_session = requests.Session()
    power_wall = Powerwall(entry.data[CONF_IP_ADDRESS],
                           http_session=http_session)
    try:
        await hass.async_add_executor_job(power_wall.detect_and_pin_version)
        powerwall_data = await hass.async_add_executor_job(
            call_base_info, power_wall)
    except (PowerwallUnreachableError, APIError, ConnectionError):
        http_session.close()
        raise ConfigEntryNotReady

    await _migrate_old_unique_ids(hass, entry_id, powerwall_data)

    async def async_update_data():
        """Fetch data from API endpoint."""
        return await hass.async_add_executor_job(_fetch_powerwall_data,
                                                 power_wall)

    coordinator = DataUpdateCoordinator(
        hass,
        _LOGGER,
        name="Powerwall site",
        update_method=async_update_data,
        update_interval=timedelta(seconds=UPDATE_INTERVAL),
    )

    hass.data[DOMAIN][entry.entry_id] = powerwall_data
    hass.data[DOMAIN][entry.entry_id].update({
        POWERWALL_OBJECT:
        power_wall,
        POWERWALL_COORDINATOR:
        coordinator,
        POWERWALL_HTTP_SESSION:
        http_session,
    })

    await coordinator.async_refresh()

    for component in PLATFORMS:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(entry, component))

    return True
Exemplo n.º 17
0
    def loginLocal(self, email, password, IPaddress):
        self.localEmail = email
        self.localPassword = password
        self.IPAddress = IPaddress
        LOGGER.debug('Local Access Supported')

        self.TPWlocal = Powerwall(IPaddress)
        self.TPWlocal.login(self.localPassword, self.localEmail)
        if not (self.TPWlocal.is_authenticated()):
            LOGGER.debug('Error Logging into Tesla Power Wall')
            self.localAccessUp = False
        else:
            self.localAccessUp = True
            self.metersDayStart = self.TPWlocal.get_meters()
            generator = self.TPWlocal._api.get('generators')
            if not (generator['generators']):
                self.generatorInstalled = False
            else:
                self.generatorInstalled = True
            solar = self.TPWlocal.get_solars()
            if solar:
                self.solarInstalled = True
            else:
                self.solarInstalled = False
Exemplo n.º 18
0
async def validate_input(hass: core.HomeAssistant, data):
    """Validate the user input allows us to connect.

    Data has the keys from DATA_SCHEMA with values provided by the user.
    """

    power_wall = Powerwall(data[CONF_IP_ADDRESS])

    try:
        site_info = await hass.async_add_executor_job(power_wall.get_site_info)
    except (PowerwallUnreachableError, ApiError, ConnectionError):
        raise CannotConnect

    # Return info that you want to store in the config entry.
    return {"title": site_info.site_name}
Exemplo n.º 19
0
def _mock_powerwall_return_value(
    site_info=None,
    charge=None,
    sitemaster=None,
    meters=None,
    grid_status=None,
    status=None,
    device_type=None,
):
    powerwall_mock = MagicMock(Powerwall("1.2.3.4"))
    powerwall_mock.get_site_info = Mock(return_value=site_info)
    powerwall_mock.get_charge = Mock(return_value=charge)
    powerwall_mock.get_sitemaster = Mock(return_value=sitemaster)
    powerwall_mock.get_meters = Mock(return_value=meters)
    powerwall_mock.get_grid_status = Mock(return_value=grid_status)
    powerwall_mock.get_status = Mock(return_value=status)
    powerwall_mock.get_device_type = Mock(return_value=device_type)

    return powerwall_mock
Exemplo n.º 20
0
async def validate_input(opp: core.OpenPeerPower, data):
    """Validate the user input allows us to connect.

    Data has the keys from schema with values provided by the user.
    """

    power_wall = Powerwall(data[CONF_IP_ADDRESS])
    password = data[CONF_PASSWORD]

    try:
        site_info = await opp.async_add_executor_job(
            _login_and_fetch_site_info, power_wall, password)
    except MissingAttributeError as err:
        # Only log the exception without the traceback
        _LOGGER.error(str(err))
        raise WrongVersion from err

    # Return info that you want to store in the config entry.
    return {"title": site_info.site_name}
Exemplo n.º 21
0
async def validate_input(hass: core.HomeAssistant,
                         data: dict[str, str]) -> dict[str, str]:
    """Validate the user input allows us to connect.

    Data has the keys from schema with values provided by the user.
    """

    power_wall = Powerwall(data[CONF_IP_ADDRESS])
    password = data[CONF_PASSWORD]

    try:
        site_info, gateway_din = await hass.async_add_executor_job(
            _login_and_fetch_site_info, power_wall, password)
    except MissingAttributeError as err:
        # Only log the exception without the traceback
        _LOGGER.error(str(err))
        raise WrongVersion from err

    # Return info that you want to store in the config entry.
    return {"title": site_info.site_name, "unique_id": gateway_din.upper()}
Exemplo n.º 22
0
async def validate_input(hass: core.HomeAssistant, data):
    """Validate the user input allows us to connect.

    Data has the keys from schema with values provided by the user.
    """

    power_wall = Powerwall(data[CONF_IP_ADDRESS])

    try:
        await hass.async_add_executor_job(power_wall.detect_and_pin_version)
        site_info = await hass.async_add_executor_job(power_wall.get_site_info)
    except PowerwallUnreachableError as err:
        raise CannotConnect from err
    except MissingAttributeError as err:
        # Only log the exception without the traceback
        _LOGGER.error(str(err))
        raise WrongVersion from err

    # Return info that you want to store in the config entry.
    return {"title": site_info.site_name}
Exemplo n.º 23
0
def _fetch_powerwall_data(power_wall: Powerwall) -> PowerwallData:
    """Process and update powerwall data."""
    try:
        backup_reserve = power_wall.get_backup_reserve_percentage()
    except MissingAttributeError:
        backup_reserve = None

    return PowerwallData(
        charge=power_wall.get_charge(),
        site_master=power_wall.get_sitemaster(),
        meters=power_wall.get_meters(),
        grid_services_active=power_wall.is_grid_services_active(),
        grid_status=power_wall.get_grid_status(),
        backup_reserve=backup_reserve,
    )
Exemplo n.º 24
0
def powerwall():   
    
    strm=""
    try:
        powerwall = Powerwall(GW_IP)   
        
        powerwall.login(GW_PASSWORD, GW_USER)

        charge=powerwall.get_charge()
        meters = powerwall.get_meters()
        meters.charge=charge
        strm = str(meters.__dict__)
    except Exception as ex:
        print("Could not access Tesla Gateway: "+ str(ex))
        return Response(str(ex), mimetype="application/json")

    return Response(strm, mimetype="application/json")
Exemplo n.º 25
0
def main():
    """
    Get credentials from vault. Poke backup gateway. Push stats to influxdb
    """
    vault = get_hvac_client()
    bg_creds = vault.secrets.kv.v1.read_secret(BG_GATEWAY_SECRETS_PATH)
    influxdb_creds = vault.secrets.kv.v1.read_secret(INFLUXDB_CREDS_PATH)

    powerwall = Powerwall(BG_GATEWAY_HOST)
    try:
        powerwall.detect_and_pin_version()
    except PowerwallUnreachableError as e:
        print(e)
        return

    _ = powerwall.login(bg_creds["email"], bg_creds["password"])
    battery_pct_charge = powerwall.get_charge()
    site_info = powerwall.get_site_info()
    meters = power_wall.get_meters()
    meter_data = meters.response
    for (meter, data) in meters.items():
        pass
    return
 def setUp(self) -> None:
     self.powerwall = Powerwall(POWERWALL_IP)
     self.powerwall.login(POWERWALL_PASSWORD)
class TestPowerwall(unittest.TestCase):
    def setUp(self) -> None:
        self.powerwall = Powerwall(POWERWALL_IP)
        self.powerwall.login(POWERWALL_PASSWORD)

    def tearDown(self) -> None:
        self.powerwall.close()

    def test_get_charge(self) -> None:
        charge = self.powerwall.get_charge()
        self.assertIsInstance(charge, float)

    def test_get_meters(self) -> None:
        meters = self.powerwall.get_meters()
        self.assertIsInstance(meters, MetersAggregates)

        self.assertIsInstance(meters.site, Meter)
        self.assertIsInstance(meters.solar, Meter)
        self.assertIsInstance(meters.battery, Meter)
        self.assertIsInstance(meters.load, Meter)
        self.assertIsInstance(meters.get_meter(MeterType.SOLAR), Meter)

        for meter_type in MeterType:
            meter = meters.get_meter(meter_type)
            meter = meters.battery
            meter.energy_exported
            meter.energy_imported
            meter.instant_power
            meter.last_communication_time
            meter.frequency
            meter.average_voltage
            meter.get_energy_exported()
            meter.get_energy_imported()
            self.assertIsInstance(meter.get_power(), float)
            self.assertIsInstance(meter.is_active(), bool)
            self.assertIsInstance(meter.is_drawing_from(), bool)
            self.assertIsInstance(meter.is_sending_to(), bool)

    def test_sitemaster(self) -> None:
        sitemaster = self.powerwall.get_sitemaster()

        self.assertIsInstance(sitemaster, SiteMaster)

        sitemaster.status
        sitemaster.is_running
        sitemaster.is_connected_to_tesla
        sitemaster.is_power_supply_mode

    def test_site_info(self) -> None:
        site_info = self.powerwall.get_site_info()

        self.assertIsInstance(site_info, SiteInfo)

        site_info.nominal_system_energy
        site_info.site_name
        site_info.timezone

    def get_grid_status(self) -> None:
        grid_status = self.powerwall.get_grid_status()
        self.assertIsInstance(grid_status, GridStatus)

    def get_status(self) -> None:
        status = self.powerwall.get_status()
        self.assertIsInstance(status, PowerwallStatus)
        status.up_time_seconds
        status.start_time
        status.version
Exemplo n.º 28
0
class PowerwallDataManager:
    """Class to manager powerwall data and relogin on failure."""
    def __init__(
        self,
        hass: HomeAssistant,
        power_wall: Powerwall,
        ip_address: str,
        password: str | None,
        runtime_data: PowerwallRuntimeData,
    ) -> None:
        """Init the data manager."""
        self.hass = hass
        self.ip_address = ip_address
        self.password = password
        self.runtime_data = runtime_data
        self.power_wall = power_wall

    @property
    def login_failed_count(self) -> int:
        """Return the current number of failed logins."""
        return self.runtime_data[POWERWALL_LOGIN_FAILED_COUNT]

    @property
    def api_changed(self) -> int:
        """Return true if the api has changed out from under us."""
        return self.runtime_data[POWERWALL_API_CHANGED]

    def _increment_failed_logins(self) -> None:
        self.runtime_data[POWERWALL_LOGIN_FAILED_COUNT] += 1

    def _clear_failed_logins(self) -> None:
        self.runtime_data[POWERWALL_LOGIN_FAILED_COUNT] = 0

    def _recreate_powerwall_login(self) -> None:
        """Recreate the login on auth failure."""
        http_session = self.runtime_data[POWERWALL_HTTP_SESSION]
        http_session.close()
        http_session = requests.Session()
        self.runtime_data[POWERWALL_HTTP_SESSION] = http_session
        self.power_wall = Powerwall(self.ip_address, http_session=http_session)
        self.power_wall.login(self.password or "")

    async def async_update_data(self) -> PowerwallData:
        """Fetch data from API endpoint."""
        # Check if we had an error before
        _LOGGER.debug("Checking if update failed")
        if self.api_changed:
            raise UpdateFailed("The powerwall api has changed")
        return await self.hass.async_add_executor_job(self._update_data)

    def _update_data(self) -> PowerwallData:
        """Fetch data from API endpoint."""
        _LOGGER.debug("Updating data")
        for attempt in range(2):
            try:
                if attempt == 1:
                    self._recreate_powerwall_login()
                data = _fetch_powerwall_data(self.power_wall)
            except PowerwallUnreachableError as err:
                raise UpdateFailed(
                    "Unable to fetch data from powerwall") from err
            except MissingAttributeError as err:
                _LOGGER.error("The powerwall api has changed: %s", str(err))
                # The error might include some important information about what exactly changed.
                persistent_notification.create(self.hass,
                                               API_CHANGED_ERROR_BODY,
                                               API_CHANGED_TITLE)
                self.runtime_data[POWERWALL_API_CHANGED] = True
                raise UpdateFailed("The powerwall api has changed") from err
            except AccessDeniedError as err:
                if attempt == 1:
                    self._increment_failed_logins()
                    raise ConfigEntryAuthFailed from err
                if self.password is None:
                    raise ConfigEntryAuthFailed from err
                raise UpdateFailed(
                    f"Login attempt {self.login_failed_count}/{MAX_LOGIN_FAILURES} failed, will retry: {err}"
                ) from err
            except APIError as err:
                raise UpdateFailed(
                    f"Updated failed due to {err}, will retry") from err
            else:
                self._clear_failed_logins()
                return data
        raise RuntimeError("unreachable")
Exemplo n.º 29
0
def _mock_powerwall_side_effect(site_info=None):
    powerwall_mock = MagicMock(Powerwall("1.2.3.4"))
    powerwall_mock.get_site_info = Mock(side_effect=site_info)
    return powerwall_mock
Exemplo n.º 30
0
def _login_and_fetch_site_info(power_wall: Powerwall, password: str):
    """Login to the powerwall and fetch the base info."""
    if password is not None:
        power_wall.login(password)
    power_wall.detect_and_pin_version()
    return power_wall.get_site_info()