async def wrapper(cls, connect):
        client = Vallox("127.0.0.1")
        client.set_values = asynctest.CoroutineMock()
        instance = connect.return_value

        protocol_mock = asynctest.create_autospec(
            websockets.WebSocketCommonProtocol)
        instance.__aenter__.side_effect = protocol_mock
        await func(cls, client, protocol_mock.return_value)
Exemplo n.º 2
0
async def async_setup(hass, config):
    """Set up the client and boot the platforms."""
    conf = config[DOMAIN]
    host = conf.get(CONF_HOST)
    name = conf.get(CONF_NAME)

    client = Vallox(host)
    state_proxy = ValloxStateProxy(hass, client)
    service_handler = ValloxServiceHandler(client, state_proxy)

    hass.data[DOMAIN] = {
        'client': client,
        'state_proxy': state_proxy,
        'name': name
    }

    for vallox_service in SERVICE_TO_METHOD:
        schema = SERVICE_TO_METHOD[vallox_service]['schema']
        hass.services.async_register(DOMAIN,
                                     vallox_service,
                                     service_handler.async_handle,
                                     schema=schema)

    # Fetch initial state once before bringing up the platforms.
    await state_proxy.async_update(None)

    hass.async_create_task(
        async_load_platform(hass, 'sensor', DOMAIN, {}, config))
    hass.async_create_task(async_load_platform(hass, 'fan', DOMAIN, {},
                                               config))

    async_track_time_interval(hass, state_proxy.async_update, SCAN_INTERVAL)

    return True
Exemplo n.º 3
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the client and boot the platforms."""
    conf = config[DOMAIN]
    host = conf.get(CONF_HOST)
    name = conf.get(CONF_NAME)

    client = Vallox(host)
    state_proxy = ValloxStateProxy(hass, client)
    service_handler = ValloxServiceHandler(client, state_proxy)

    hass.data[DOMAIN] = {"client": client, "state_proxy": state_proxy, "name": name}

    for vallox_service, method in SERVICE_TO_METHOD.items():
        schema = method["schema"]
        hass.services.async_register(
            DOMAIN, vallox_service, service_handler.async_handle, schema=schema
        )

    # The vallox hardware expects quite strict timings for websocket requests. Timings that machines
    # with less processing power, like Raspberries, cannot live up to during the busy start phase of
    # Home Asssistant. Hence, async_add_entities() for fan and sensor in respective code will be
    # called with update_before_add=False to intentionally delay the first request, increasing
    # chance that it is issued only when the machine is less busy again.
    hass.async_create_task(async_load_platform(hass, "sensor", DOMAIN, {}, config))
    hass.async_create_task(async_load_platform(hass, "fan", DOMAIN, {}, config))

    async_track_time_interval(hass, state_proxy.async_update, STATE_PROXY_SCAN_INTERVAL)

    return True
class TestValloxGetTemperature(TestCase):
  def setUp(self):
    self.client = Vallox('127.0.0.1')


  def checkGetTemperatureForProfile(self, fetch_metrics_result, profile, expected_temperature):
    self.client.fetch_metrics = mock.MagicMock(return_value=fetch_metrics_result)

    self.assertEqual(self.client.get_temperature(profile), expected_temperature)

    self.client.fetch_metrics.assert_called_once_with(list(fetch_metrics_result.keys()))

  def testGetTemperatureForProfileHome(self):
    self.checkGetTemperatureForProfile({
      'A_CYC_HOME_AIR_TEMP_TARGET': 19
    }, PROFILE.HOME, 19)

  @mock.patch('vallox_websocket_api.client.websocket.create_connection', autospec=True)
  def testFetchMetric(self, mock_websocket_create_connection):
    """
    IoQueue.KItemTypeFetch = 3
    VlxDevConstants.WS_WEB_UI_COMMAND_READ_TABLES; = 246
    item.value = 0;
    checksum = 249
    Uint16Array(4) [3, 246, 0, 249]
    """
    client = Vallox('127.0.0.1')

    ws = mock.Mock()
    ws.recv.return_value = binascii.unhexlify('0024000000000000000000000000000001000800030000000000000061df98b100030003203fb9500331000000000000000000560000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b000f732a6ca969a171d1730800010000022700000028000000000000000001a6029e000100000028ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000057c503e8ffffffffffff000000190000000000010000000000000000000300001b98012000a50000000000000000001e00010000000100000000000000000007001b000f001700010012000200070044000000010000000000000007003200320001000000000000001e0000c0a80501ffffff0000000000000000000000000000000000000000000000000000000000c0a8050c86076097f78844b7ac4db61e502fe4f2004c004c000100c00101001c001e000a00320000003703840000708f00320032000a0000000000010000000a721f0000000000010000000f728300000000000000000064715700000000000000000000000000000000000000010037001e000000000000000068bf71bb000083910000002600b4000000010001000000010001001e000f00080001001200000003000000000000000000000017000003e90000000000000001000100010000000a003200010000000000000000000000000000000000000000001000000000000000000000000000540048000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
    mock_websocket_create_connection.return_value = ws

    self.assertEqual(20.0, client.fetch_metric('A_CYC_HOME_AIR_TEMP_TARGET'))

    ws.send.assert_called_once_with(struct.pack( "HHHH", 3, 246, 0, 249), opcode=0x2)
Exemplo n.º 5
0
async def validate_host(hass: HomeAssistant, host: str) -> None:
    """Validate that the user input allows us to connect."""

    if not is_ip_address(host):
        raise InvalidHost(f"Invalid IP address: {host}")

    client = Vallox(host)
    await client.get_info()
  def testFetchMetric(self, mock_websocket_create_connection):
    """
    IoQueue.KItemTypeFetch = 3
    VlxDevConstants.WS_WEB_UI_COMMAND_READ_TABLES; = 246
    item.value = 0;
    checksum = 249
    Uint16Array(4) [3, 246, 0, 249]
    """
    client = Vallox('127.0.0.1')

    ws = mock.Mock()
    ws.recv.return_value = binascii.unhexlify('0024000000000000000000000000000001000800030000000000000061df98b100030003203fb9500331000000000000000000560000000000000000000000000000000000000000001b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002b000f732a6ca969a171d1730800010000022700000028000000000000000001a6029e000100000028ffffffffffffffffffffffffffffffffffffffffffffffff000000000000000057c503e8ffffffffffff000000190000000000010000000000000000000300001b98012000a50000000000000000001e00010000000100000000000000000007001b000f001700010012000200070044000000010000000000000007003200320001000000000000001e0000c0a80501ffffff0000000000000000000000000000000000000000000000000000000000c0a8050c86076097f78844b7ac4db61e502fe4f2004c004c000100c00101001c001e000a00320000003703840000708f00320032000a0000000000010000000a721f0000000000010000000f728300000000000000000064715700000000000000000000000000000000000000010037001e000000000000000068bf71bb000083910000002600b4000000010001000000010001001e000f00080001001200000003000000000000000000000017000003e90000000000000001000100010000000a003200010000000000000000000000000000000000000000001000000000000000000000000000540048000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
    mock_websocket_create_connection.return_value = ws

    self.assertEqual(20.0, client.fetch_metric('A_CYC_HOME_AIR_TEMP_TARGET'))

    ws.send.assert_called_once_with(struct.pack( "HHHH", 3, 246, 0, 249), opcode=0x2)
  def testFetchRawLogs(self, mock_websocket_create_connection):
    """
    IoQueue.KItemTypeLog = 1
    VlxDevConstants.WS_WEB_UI_COMMAND_LOG_RAW = 243
    checksum = 245
    Uint16Array(3) [2, 243, 245]
    :return:
    """
    client = Vallox('127.0.0.1')

    ws = mock.Mock()
    ws.recv.return_value = struct.pack("HHHH", 3, 243, 0, 246)
    mock_websocket_create_connection.return_value = ws

    data = client.fetch_raw_logs()

    ws.send.assert_called_once_with(struct.pack( "HHH", 2, 243, 245), opcode=0x2)
class TestValloxSetTemperature(TestCase):
  def setUp(self):
    self.client = Vallox('127.0.0.1')

    self.client.set_values = mock.MagicMock()

  def checkSetTemperature(self, profile, temperature, set_values_dict):
    self.client.set_temperature(profile, temperature)

    self.client.set_values.assert_called_once_with(set_values_dict)

  def testSetTemperatureHome(self):
    self.checkSetTemperature(PROFILE.HOME, 19, {'A_CYC_HOME_AIR_TEMP_TARGET': 19})

  def testSetTemperatureAway(self):
    self.checkSetTemperature(PROFILE.AWAY, 19, {'A_CYC_AWAY_AIR_TEMP_TARGET': 19})

  def testSetTemperatureBoost(self):
    self.checkSetTemperature(PROFILE.BOOST, 19, {'A_CYC_BOOST_AIR_TEMP_TARGET': 19})

  def testSetTemperatureWrong(self):
    with self.assertRaises(AttributeError) as context:
      self.checkSetTemperature(PROFILE.FIREPLACE, 19, {'A_CYC_FIREPLACE_AIR_TEMP_TARGET': 19})
Exemplo n.º 9
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up the client and boot the platforms."""
    host = entry.data[CONF_HOST]
    name = entry.data[CONF_NAME]

    client = Vallox(host)

    async def async_update_data() -> ValloxState:
        """Fetch state update."""
        _LOGGER.debug("Updating Vallox state cache")

        try:
            metric_cache = await client.fetch_metrics()
            profile = await client.get_profile()

        except (OSError, ValloxApiException) as err:
            raise UpdateFailed("Error during state cache update") from err

        return ValloxState(metric_cache, profile)

    coordinator = ValloxDataUpdateCoordinator(
        hass,
        _LOGGER,
        name=f"{name} DataUpdateCoordinator",
        update_interval=STATE_SCAN_INTERVAL,
        update_method=async_update_data,
    )

    await coordinator.async_config_entry_first_refresh()

    service_handler = ValloxServiceHandler(client, coordinator)
    for vallox_service, service_details in SERVICE_TO_METHOD.items():
        hass.services.async_register(
            DOMAIN,
            vallox_service,
            service_handler.async_handle,
            schema=service_details.schema,
        )

    hass.data.setdefault(DOMAIN, {})[entry.entry_id] = {
        "client": client,
        "coordinator": coordinator,
        "name": name,
    }

    await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

    return True
Exemplo n.º 10
0
 def setUp(self):
     self.client = Vallox("127.0.0.1")
Exemplo n.º 11
0
    def setUp(self):
        self.client = Vallox("127.0.0.1")

        self.client.set_values = asynctest.CoroutineMock()
Exemplo n.º 12
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the client and boot the platforms."""
    conf = config[DOMAIN]
    host = conf[CONF_HOST]
    name = conf[CONF_NAME]

    client = Vallox(host)

    async def async_update_data() -> ValloxState:
        """Fetch state update."""
        _LOGGER.debug("Updating Vallox state cache")

        try:
            metric_cache = await client.fetch_metrics()
            profile = await client.get_profile()

        except (OSError, ValloxApiException) as err:
            raise UpdateFailed("Error during state cache update") from err

        return ValloxState(metric_cache, profile)

    coordinator = ValloxDataUpdateCoordinator(
        hass,
        _LOGGER,
        name=f"{name} DataUpdateCoordinator",
        update_interval=STATE_SCAN_INTERVAL,
        update_method=async_update_data,
    )

    service_handler = ValloxServiceHandler(client, coordinator)
    for vallox_service, service_details in SERVICE_TO_METHOD.items():
        hass.services.async_register(
            DOMAIN,
            vallox_service,
            service_handler.async_handle,
            schema=service_details.schema,
        )

    hass.data[DOMAIN] = {
        "client": client,
        "coordinator": coordinator,
        "name": name
    }

    async def _async_load_platform_delayed(*_: Any) -> None:
        await coordinator.async_refresh()
        hass.async_create_task(
            async_load_platform(hass, "sensor", DOMAIN, {}, config))
        hass.async_create_task(
            async_load_platform(hass, "fan", DOMAIN, {}, config))

    # The Vallox hardware expects quite strict timings for websocket requests. Timings that machines
    # with less processing power, like a Raspberry Pi, cannot live up to during the busy start phase
    # of Home Asssistant.
    #
    # Hence, wait for the started event before doing a first data refresh and loading the platforms,
    # because it usually means the system is less busy after the event and can now meet the
    # websocket timing requirements.
    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED,
                               _async_load_platform_delayed)

    return True
 def setUp(self):
     self.client = Vallox('127.0.0.1')
class TestValloxGetProfile(TestCase):
    def setUp(self):
        self.client = Vallox('127.0.0.1')

    def checkGetProfile(self, fetch_metrics_result, expected_profile):
        self.client.fetch_metrics = mock.MagicMock(
            return_value=fetch_metrics_result)

        self.assertEqual(self.client.get_profile(), expected_profile)

        self.client.fetch_metrics.assert_called_once_with([
            'A_CYC_STATE', 'A_CYC_BOOST_TIMER', 'A_CYC_FIREPLACE_TIMER',
            'A_CYC_EXTRA_TIMER'
        ])

    def testGetProfileHome(self):
        self.checkGetProfile(
            {
                'A_CYC_STATE': 0,
                'A_CYC_BOOST_TIMER': 0,
                'A_CYC_FIREPLACE_TIMER': 0,
                'A_CYC_EXTRA_TIMER': 0
            }, PROFILE.HOME)

    def testGetProfileAway(self):
        self.checkGetProfile(
            {
                'A_CYC_STATE': 1,
                'A_CYC_BOOST_TIMER': 0,
                'A_CYC_FIREPLACE_TIMER': 0,
                'A_CYC_EXTRA_TIMER': 0
            }, PROFILE.AWAY)

    def testGetProfileBoost(self):
        self.checkGetProfile(
            {
                'A_CYC_STATE': 0,
                'A_CYC_BOOST_TIMER': 30,
                'A_CYC_FIREPLACE_TIMER': 0,
                'A_CYC_EXTRA_TIMER': 0
            }, PROFILE.BOOST)

        self.checkGetProfile(
            {
                'A_CYC_STATE': 1,
                'A_CYC_BOOST_TIMER': 30,
                'A_CYC_FIREPLACE_TIMER': 0,
                'A_CYC_EXTRA_TIMER': 0
            }, PROFILE.BOOST)

    def testGetProfileFireplace(self):
        self.checkGetProfile(
            {
                'A_CYC_STATE': 0,
                'A_CYC_BOOST_TIMER': 0,
                'A_CYC_FIREPLACE_TIMER': 30,
                'A_CYC_EXTRA_TIMER': 0
            }, PROFILE.FIREPLACE)

        self.checkGetProfile(
            {
                'A_CYC_STATE': 1,
                'A_CYC_BOOST_TIMER': 0,
                'A_CYC_FIREPLACE_TIMER': 30,
                'A_CYC_EXTRA_TIMER': 0
            }, PROFILE.FIREPLACE)

    def testGetProfileExtra(self):
        self.checkGetProfile(
            {
                'A_CYC_STATE': 0,
                'A_CYC_BOOST_TIMER': 0,
                'A_CYC_FIREPLACE_TIMER': 0,
                'A_CYC_EXTRA_TIMER': 30
            }, PROFILE.EXTRA)

        self.checkGetProfile(
            {
                'A_CYC_STATE': 1,
                'A_CYC_BOOST_TIMER': 0,
                'A_CYC_FIREPLACE_TIMER': 0,
                'A_CYC_EXTRA_TIMER': 30
            }, PROFILE.EXTRA)
    def setUp(self):
        self.client = Vallox('127.0.0.1')

        self.client.set_values = mock.MagicMock()
class TestValloxSetProfile(TestCase):
    def setUp(self):
        self.client = Vallox('127.0.0.1')

        self.client.set_values = mock.MagicMock()

    def checkSetProfile(self, profile, set_values_dict):
        self.client.set_profile(profile)

        self.client.set_values.assert_called_once_with(set_values_dict)

    def testSetProfileHome(self):
        self.checkSetProfile(
            PROFILE.HOME, {
                'A_CYC_STATE': '0',
                'A_CYC_BOOST_TIMER': '0',
                'A_CYC_FIREPLACE_TIMER': '0',
                'A_CYC_EXTRA_TIMER': '0'
            })

    def testSetProfileAway(self):
        self.checkSetProfile(
            PROFILE.AWAY, {
                'A_CYC_STATE': '1',
                'A_CYC_BOOST_TIMER': '0',
                'A_CYC_FIREPLACE_TIMER': '0',
                'A_CYC_EXTRA_TIMER': '0'
            })

    def testSetProfileBoost(self):
        self.client.fetch_metric = mock.MagicMock(return_value=30)

        self.checkSetProfile(
            PROFILE.BOOST, {
                'A_CYC_BOOST_TIMER': '30',
                'A_CYC_FIREPLACE_TIMER': '0',
                'A_CYC_EXTRA_TIMER': '0'
            })

        self.client.fetch_metric.assert_called_once_with('A_CYC_BOOST_TIME')

    def testSetProfileFireplace(self):
        self.client.fetch_metric = mock.MagicMock(return_value=30)

        self.checkSetProfile(
            PROFILE.FIREPLACE, {
                'A_CYC_BOOST_TIMER': '0',
                'A_CYC_FIREPLACE_TIMER': '30',
                'A_CYC_EXTRA_TIMER': '0'
            })

        self.client.fetch_metric.assert_called_once_with(
            'A_CYC_FIREPLACE_TIME')

    def testSetProfileExtra(self):
        self.client.fetch_metric = mock.MagicMock(return_value=30)

        self.checkSetProfile(
            PROFILE.EXTRA, {
                'A_CYC_BOOST_TIMER': '0',
                'A_CYC_FIREPLACE_TIMER': '0',
                'A_CYC_EXTRA_TIMER': '30'
            })

        self.client.fetch_metric.assert_called_once_with('A_CYC_EXTRA_TIME')