Exemple #1
0
async def main_loop(loop, password, user, ip):  # pylint: disable=invalid-name
    """Main loop."""
    async with aiohttp.ClientSession(
            loop=loop, connector=aiohttp.TCPConnector(ssl=False)) as session:
        VAR["sma"] = pysma.SMA(session, ip, password=password, group=user)
        await VAR["sma"].new_session()
        if VAR["sma"].sma_sid is None:
            _LOGGER.info("No session ID")
            return

        _LOGGER.info("NEW SID: %s", VAR["sma"].sma_sid)

        VAR["running"] = True
        cnt = 5
        sensors = pysma.Sensors()
        while VAR.get("running"):
            await VAR["sma"].read(sensors)
            print_table(sensors)
            send_table(sensors)
            cnt -= 1
            if cnt == 0:
                break
            await asyncio.sleep(2)

        await VAR["sma"].close_session()
Exemple #2
0
    async def test_new_session_fail(self, mock_aioresponse):  # noqa: F811
        mock_aioresponse.post(f"{self.base_url}/dyn/login.json",
                              payload={"result": {}})

        session = aiohttp.ClientSession()
        sma = pysma.SMA(session, self.host, "pass")
        assert not await sma.new_session()
Exemple #3
0
    async def test_session(self, mock_warn, mock_aioresponse):  # noqa: F811
        mock_aioresponse.post(f"{self.base_url}/dyn/login.json",
                              payload={"result": {
                                  "sid": "ABCD"
                              }})
        session = aiohttp.ClientSession()
        sma = pysma.SMA(session, self.host, "extralongpassword")
        assert await sma.new_session()

        assert mock_warn.call_count == 1
        await sma.close_session()
        await sma.close_session()

        assert mock_warn.call_count == 1
Exemple #4
0
 async def test_device_info_fail(self, mock_aioresponse):  # noqa: F811
     mock_aioresponse.post(f"{self.base_url}/dyn/login.json",
                           payload={"result": {
                               "sid": "ABCD"
                           }})
     mock_aioresponse.post(
         f"{self.base_url}/dyn/getValues.json?sid=ABCD",
         payload={},
     )
     session = aiohttp.ClientSession()
     sma = pysma.SMA(session, self.host, "pass")
     assert await sma.new_session()
     result = await sma.device_info()
     assert not result
Exemple #5
0
async def validate_input(hass: core.HomeAssistant,
                         data: dict[str, Any]) -> dict[str, Any]:
    """Validate the user input allows us to connect."""
    session = async_get_clientsession(hass, verify_ssl=data[CONF_VERIFY_SSL])

    protocol = "https" if data[CONF_SSL] else "http"
    url = f"{protocol}://{data[CONF_HOST]}"

    sma = pysma.SMA(session, url, data[CONF_PASSWORD], group=data[CONF_GROUP])

    # new_session raises SmaAuthenticationException on failure
    await sma.new_session()
    device_info = await sma.device_info()
    await sma.close_session()

    return device_info
Exemple #6
0
 async def test_device_info(self, mock_aioresponse):  # noqa: F811
     mock_aioresponse.post(f"{self.base_url}/dyn/login.json",
                           payload={"result": {
                               "sid": "ABCD"
                           }})
     mock_aioresponse.post(
         f"{self.base_url}/dyn/getValues.json?sid=ABCD",
         payload={
             "result": {
                 "0199-xxxxx385": {
                     "6800_08822B00": {
                         "1": [{
                             "validVals": [461],
                             "val": [{
                                 "tag": 461
                             }]
                         }]
                     },
                     "6800_00A21E00": {
                         "1": [{
                             "low": 0,
                             "high": None,
                             "val": MOCK_DEVICE["serial"]
                         }]
                     },
                     "6800_08822000": {
                         "1": [{
                             "validVals": [9401, 9402, 9403, 9404, 9405],
                             "val": [{
                                 "tag": 9402
                             }],
                         }]
                     },
                     "6800_10821E00": {
                         "1": [{
                             "val": MOCK_DEVICE["name"]
                         }]
                     },
                 }
             }
         },
     )
     session = aiohttp.ClientSession()
     sma = pysma.SMA(session, self.host, "pass")
     result = await sma.device_info()
     assert result
     assert result == MOCK_DEVICE
Exemple #7
0
def main(loop, password, ip):
    """Main loop."""
    session = aiohttp.ClientSession(loop=loop)
    sma = pysma.SMA(session,
                    ip,
                    password=password,
                    group=pysma.GROUP_INSTALLER)
    yield from sma.new_session()
    _LOGGER.info("NEW SID: %s", sma._sma_sid)

    while loop.jk_run:
        res = yield from sma.read(
            [pysma.KEY_CURRENT_CONSUMPTION_W, pysma.KEY_CURRENT_POWER_W])
        _LOGGER.info(res)
        yield from asyncio.sleep(1)

    yield from sma.close_session()
    yield from session.close()
Exemple #8
0
async def main(loop, address, password, host, interval):
    """Main loop."""
    session = aiohttp.ClientSession(loop=loop)
    sma = pysma.SMA(session, address, password=password, group="user")
    await sma.new_session()

    atexit.register(await cleanup, sma, session)

    while True:
        try:
            with async_timeout.timeout(20):
                res = await sma.read([m["sensor"] for m in METRICS])
            putval(res)
        except asyncio.TimeoutError:
            _LOGGER.error(traceback.format_exc())
            _LOGGER.error("Timeout when talking to SMA inverter")
            sys.exit(3)

        await asyncio.sleep(interval)
Exemple #9
0
async def validate_input(hass: core.HomeAssistant,
                         data: dict[str, Any]) -> dict[str, Any]:
    """Validate the user input allows us to connect."""
    session = async_get_clientsession(hass, verify_ssl=data[CONF_VERIFY_SSL])

    protocol = "https" if data[CONF_SSL] else "http"
    url = f"{protocol}://{data[CONF_HOST]}"

    sma = pysma.SMA(session, url, data[CONF_PASSWORD], group=data[CONF_GROUP])

    if await sma.new_session() is False:
        raise InvalidAuth

    device_info = await sma.device_info()
    await sma.close_session()

    if not device_info:
        raise CannotRetrieveDeviceInfo

    return device_info
Exemple #10
0
async def main_loop(loop, password, user, ip):  # pylint: disable=invalid-name
    """Main loop."""
    async with aiohttp.ClientSession(loop=loop) as session:
        VAR['sma'] = pysma.SMA(session, ip, password=password, group=user)
        await VAR['sma'].new_session()
        if VAR['sma'].sma_sid is None:
            _LOGGER.info("No session ID")
            return

        _LOGGER.info("NEW SID: %s", VAR['sma'].sma_sid)

        VAR['running'] = True
        cnt = 5
        while VAR.get('running'):
            await VAR['sma'].read(pysma.SENSORS)
            cnt -= 1
            if cnt == 0:
                break
            await asyncio.sleep(2)

        await VAR['sma'].close_session()
Exemple #11
0
async def main_loop(password, user, url):
    """Run main loop."""
    async with aiohttp.ClientSession(
        connector=aiohttp.TCPConnector(ssl=False)
    ) as session:
        VAR["sma"] = pysma.SMA(session, url, password=password, group=user)

        try:
            await VAR["sma"].new_session()
        except pysma.exceptions.SmaAuthenticationException:
            _LOGGER.warning("Authentication failed!")
            return
        except pysma.exceptions.SmaConnectionException:
            _LOGGER.warning("Unable to connect to device at %s", url)
            return

        # We should not get any exceptions, but if we do we will close the session.
        try:
            VAR["running"] = True
            cnt = 5
            sensors = await VAR["sma"].get_sensors()
            device_info = await VAR["sma"].device_info()

            for name, value in device_info.items():
                print("{:>15}{:>25}".format(name, value))

            # enable all sensors
            for sensor in sensors:
                sensor.enabled = True

            while VAR.get("running"):
                await VAR["sma"].read(sensors)
                print_table(sensors)
                cnt -= 1
                if cnt == 0:
                    break
                await asyncio.sleep(2)
        finally:
            _LOGGER.info("Closing Session...")
            await VAR["sma"].close_session()
Exemple #12
0
 async def test_device_info_fallback(self, mock_aioresponse):  # noqa: F811
     mock_aioresponse.post(f"{self.base_url}/dyn/login.json",
                           payload={"result": {
                               "sid": "ABCD"
                           }})
     mock_aioresponse.post(
         f"{self.base_url}/dyn/getValues.json?sid=ABCD",
         payload={
             "result": {
                 "0199-xxxxx385": {
                     "6800_08822B00": {
                         "1": [{
                             "validVals": [461],
                             "val": [{
                                 "tag": 461
                             }]
                         }]
                     },
                     "6800_10821E00": {
                         "1": [{
                             "val": MOCK_DEVICE["name"]
                         }]
                     },
                 }
             }
         },
     )
     session = aiohttp.ClientSession()
     sma = pysma.SMA(session, self.host, "pass")
     assert await sma.new_session()
     result = await sma.device_info()
     assert result
     assert result["manufacturer"] == "SMA"
     assert result["name"] == MOCK_DEVICE["name"]
     assert result["type"] == ""
     assert result["serial"] == "9999999999"
Exemple #13
0
def async_setup_platform(hass,
                         config,
                         async_add_entities,
                         discovery_info=None):
    """Set up SMA WebConnect sensor."""
    import pysma

    # Sensor_defs from the library
    sensor_defs = dict(
        zip(SENSOR_OPTIONS, [(pysma.KEY_CURRENT_CONSUMPTION_W, 'W', 1),
                             (pysma.KEY_CURRENT_POWER_W, 'W', 1),
                             (pysma.KEY_TOTAL_CONSUMPTION_KWH, 'kWh', 1000),
                             (pysma.KEY_TOTAL_YIELD_KWH, 'kWh', 1000)]))

    # Sensor_defs from the custom config
    for name, prop in config[CONF_CUSTOM].items():
        if name in sensor_defs:
            _LOGGER.warning("Custom sensor %s replace built-in sensor", name)
        sensor_defs[name] = (prop['key'], prop['unit'], prop['factor'])

    # Prepare all HASS sensor entities
    hass_sensors = []
    used_sensors = []
    for name, attr in config[CONF_SENSORS].items():
        hass_sensors.append(SMAsensor(name, attr, sensor_defs))
        used_sensors.append(name)
        used_sensors.extend(attr)

    # Remove sensor_defs not in use
    sensor_defs = {
        name: val
        for name, val in sensor_defs.items() if name in used_sensors
    }

    async_add_entities(hass_sensors)

    # Init the SMA interface
    session = async_get_clientsession(hass)
    grp = {
        GROUP_INSTALLER: pysma.GROUP_INSTALLER,
        GROUP_USER: pysma.GROUP_USER
    }[config[CONF_GROUP]]

    url = "http{}://{}".format("s" if config[CONF_SSL] else "",
                               config[CONF_HOST])

    sma = pysma.SMA(session, url, config[CONF_PASSWORD], group=grp)

    # Ensure we logout on shutdown
    @asyncio.coroutine
    def async_close_session(event):
        """Close the session."""
        yield from sma.close_session()

    hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, async_close_session)

    # Read SMA values periodically & update sensors
    names_to_query = list(sensor_defs.keys())
    keys_to_query = [sensor_defs[name][0] for name in names_to_query]

    backoff = 0

    @asyncio.coroutine
    def async_sma(event):
        """Update all the SMA sensors."""
        nonlocal backoff
        if backoff > 1:
            backoff -= 1
            return

        values = yield from sma.read(keys_to_query)
        if values is None:
            backoff = 3
            return
        values = [0 if val is None else val for val in values]
        res = dict(zip(names_to_query, values))
        res = {key: val // sensor_defs[key][2] for key, val in res.items()}
        _LOGGER.debug("Update sensors %s %s %s", keys_to_query, values, res)
        tasks = []
        for sensor in hass_sensors:
            task = sensor.async_update_values(res)
            if task:
                tasks.append(task)
        if tasks:
            yield from asyncio.wait(tasks, loop=hass.loop)

    interval = config.get(CONF_SCAN_INTERVAL) or timedelta(seconds=5)
    async_track_time_interval(hass, async_sma, interval)
Exemple #14
0
 async def test_init(self):
     """Initialize & close the SMA transport class."""
     aiosession = None
     sma = pysma.SMA(aiosession, "192.168.0.100", "pass")
     await sma.close_session()
Exemple #15
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up sma from a config entry."""
    # Init the SMA interface
    protocol = "https" if entry.data[CONF_SSL] else "http"
    url = f"{protocol}://{entry.data[CONF_HOST]}"
    verify_ssl = entry.data[CONF_VERIFY_SSL]
    group = entry.data[CONF_GROUP]
    password = entry.data[CONF_PASSWORD]

    session = async_get_clientsession(hass, verify_ssl=verify_ssl)
    sma = pysma.SMA(session, url, password, group)

    try:
        # Get updated device info
        sma_device_info = await sma.device_info()
        # Get all device sensors
        sensor_def = await sma.get_sensors()
    except (
            pysma.exceptions.SmaReadException,
            pysma.exceptions.SmaConnectionException,
    ) as exc:
        raise ConfigEntryNotReady from exc

    if TYPE_CHECKING:
        assert entry.unique_id

    # Create DeviceInfo object from sma_device_info
    device_info = DeviceInfo(
        configuration_url=url,
        identifiers={(DOMAIN, entry.unique_id)},
        manufacturer=sma_device_info["manufacturer"],
        model=sma_device_info["type"],
        name=sma_device_info["name"],
        sw_version=sma_device_info["sw_version"],
    )

    # Define the coordinator
    async def async_update_data():
        """Update the used SMA sensors."""
        try:
            await sma.read(sensor_def)
        except (
                pysma.exceptions.SmaReadException,
                pysma.exceptions.SmaConnectionException,
        ) as exc:
            raise UpdateFailed(exc) from exc

    interval = timedelta(
        seconds=entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL))

    coordinator = DataUpdateCoordinator(
        hass,
        _LOGGER,
        name="sma",
        update_method=async_update_data,
        update_interval=interval,
    )

    try:
        await coordinator.async_config_entry_first_refresh()
    except ConfigEntryNotReady:
        await sma.close_session()
        raise

    # Ensure we logout on shutdown
    async def async_close_session(event):
        """Close the session."""
        await sma.close_session()

    remove_stop_listener = hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP,
                                                      async_close_session)

    hass.data.setdefault(DOMAIN, {})
    hass.data[DOMAIN][entry.entry_id] = {
        PYSMA_OBJECT: sma,
        PYSMA_COORDINATOR: coordinator,
        PYSMA_SENSORS: sensor_def,
        PYSMA_REMOVE_LISTENER: remove_stop_listener,
        PYSMA_DEVICE_INFO: device_info,
    }

    await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

    return True
Exemple #16
0
async def async_setup_platform(hass,
                               config,
                               async_add_entities,
                               discovery_info=None):
    """Set up SMA WebConnect sensor."""
    import pysma

    # Check config again during load - dependency available
    config = _check_sensor_schema(config)

    # Init all default sensors
    sensor_def = pysma.Sensors()

    # Sensor from the custom config
    sensor_def.add([
        pysma.Sensor(o[CONF_KEY], n, o[CONF_UNIT], o[CONF_FACTOR],
                     o.get(CONF_PATH)) for n, o in config[CONF_CUSTOM].items()
    ])

    # Use all sensors by default
    config_sensors = config[CONF_SENSORS]
    if not config_sensors:
        config_sensors = {s.name: [] for s in sensor_def}

    # Prepare all HASS sensor entities
    hass_sensors = []
    used_sensors = []
    for name, attr in config_sensors.items():
        sub_sensors = [sensor_def[s] for s in attr]
        hass_sensors.append(SMAsensor(sensor_def[name], sub_sensors))
        used_sensors.append(name)
        used_sensors.extend(attr)

    async_add_entities(hass_sensors)
    used_sensors = [sensor_def[s] for s in set(used_sensors)]

    # Init the SMA interface
    session = async_get_clientsession(hass, verify_ssl=config[CONF_VERIFY_SSL])
    grp = config[CONF_GROUP]

    url = "http{}://{}".format("s" if config[CONF_SSL] else "",
                               config[CONF_HOST])

    sma = pysma.SMA(session, url, config[CONF_PASSWORD], group=grp)

    # Ensure we logout on shutdown
    async def async_close_session(event):
        """Close the session."""
        await sma.close_session()

    hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, async_close_session)

    backoff = 0
    backoff_step = 0

    async def async_sma(event):
        """Update all the SMA sensors."""
        nonlocal backoff, backoff_step
        if backoff > 1:
            backoff -= 1
            return

        values = await sma.read(used_sensors)
        if not values:
            try:
                backoff = [1, 1, 1, 6, 30][backoff_step]
                backoff_step += 1
            except IndexError:
                backoff = 60
            return
        backoff_step = 0

        tasks = []
        for sensor in hass_sensors:
            task = sensor.async_update_values()
            if task:
                tasks.append(task)
        if tasks:
            await asyncio.wait(tasks)

    interval = config.get(CONF_SCAN_INTERVAL) or timedelta(seconds=5)
    async_track_time_interval(hass, async_sma, interval)
Exemple #17
0
 def test_init(self):
     """Initialize & close the SMA transport class."""
     aiosession = None
     sma = pysma.SMA(aiosession, '192.168.0.100', 'pass')
     sma.close_session()
Exemple #18
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up sma from a config entry."""
    # Init all default sensors
    sensor_def = pysma.Sensors()

    if entry.source == SOURCE_IMPORT:
        config_sensors = _parse_legacy_options(entry, sensor_def)
        _migrate_old_unique_ids(hass, entry, sensor_def, config_sensors)

    # Init the SMA interface
    protocol = "https" if entry.data[CONF_SSL] else "http"
    url = f"{protocol}://{entry.data[CONF_HOST]}"
    verify_ssl = entry.data[CONF_VERIFY_SSL]
    group = entry.data[CONF_GROUP]
    password = entry.data[CONF_PASSWORD]

    session = async_get_clientsession(hass, verify_ssl=verify_ssl)
    sma = pysma.SMA(session, url, password, group)

    # Define the coordinator
    async def async_update_data():
        """Update the used SMA sensors."""
        values = await sma.read(sensor_def)
        if not values:
            raise UpdateFailed

    interval = timedelta(
        seconds=entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
    )

    coordinator = DataUpdateCoordinator(
        hass,
        _LOGGER,
        name="sma",
        update_method=async_update_data,
        update_interval=interval,
    )

    try:
        await coordinator.async_config_entry_first_refresh()
    except ConfigEntryNotReady:
        await sma.close_session()
        raise

    # Ensure we logout on shutdown
    async def async_close_session(event):
        """Close the session."""
        await sma.close_session()

    remove_stop_listener = hass.bus.async_listen_once(
        EVENT_HOMEASSISTANT_STOP, async_close_session
    )

    hass.data.setdefault(DOMAIN, {})
    hass.data[DOMAIN][entry.entry_id] = {
        PYSMA_OBJECT: sma,
        PYSMA_COORDINATOR: coordinator,
        PYSMA_SENSORS: sensor_def,
        PYSMA_REMOVE_LISTENER: remove_stop_listener,
    }

    hass.config_entries.async_setup_platforms(entry, PLATFORMS)

    return True
Exemple #19
0
 async def test_new_session_invalid_group(self,
                                          mock_aioresponse):  # noqa: F811
     session = aiohttp.ClientSession()
     with pytest.raises(KeyError):
         pysma.SMA(session, self.host, "pass", "invalid-group")