diff --git a/.gitignore b/.gitignore index fcf4152..3302ae1 100644 --- a/.gitignore +++ b/.gitignore @@ -141,3 +141,5 @@ dmypy.json # Pyre type checker .pyre/ /.tool-versions + +/pyflichub/version.py diff --git a/main.py b/main.py index 3e2414d..394d739 100644 --- a/main.py +++ b/main.py @@ -8,11 +8,12 @@ from pyflichub.client import FlicHubTcpClient from pyflichub.command import Command from pyflichub.event import Event +from pyflichub.network import Network logging.basicConfig(level=logging.DEBUG) CLIENT_READY_TIMEOUT = 10.0 -HOST = ('192.168.1.64', 8124) +HOST = ('192.168.1.249', 8124) def event_callback(button: FlicButton, event: Event): @@ -45,10 +46,16 @@ def client_disconnected(): print(f"Client not connected after {CLIENT_READY_TIMEOUT} secs so terminating") exit() - buttons = await client.get_buttons() + buttons: [FlicButton] = await client.get_buttons() for button in buttons: print(f"Button name: {button.name} - Connected: {button.connected}") + network: Network = await client.get_network() + if network.has_wifi(): + print(f"Wifi State: {network.wifi.state} - Connected: {network.wifi.connected}") + if network.has_ethernet(): + print(f"Ethernet IP: {network.ethernet.ip} - Connected: {network.ethernet.connected}") + # for button in buttons: # print(f"Button name: {button.name} - Battery: {await client.get_battery_status(button.bdaddr)}") diff --git a/pyflichub/button.py b/pyflichub/button.py index a0ae96e..056fa2e 100644 --- a/pyflichub/button.py +++ b/pyflichub/button.py @@ -1,8 +1,10 @@ -class FlicButton(dict): +from datetime import datetime + + +class FlicButton(): def __init__(self, bdaddr: str, serial_number: str, color: str, name: str, active_disconnect: bool, connected: bool, ready: bool, battery_status: int, uuid: str, flic_version: int, firmware_version: int, key: str, - passive_mode: bool) -> None: - super().__init__() + passive_mode: bool, battery_timestamp: datetime) -> None: self.bdaddr = bdaddr self.serial_number = serial_number self.color = color @@ -16,3 +18,4 @@ def __init__(self, bdaddr: str, serial_number: str, color: str, name: str, activ self.firmware_version = firmware_version self.key = key self.passive_mode = passive_mode + self.battery_timestamp = battery_timestamp diff --git a/pyflichub/client.py b/pyflichub/client.py index 00e3cff..cad6969 100644 --- a/pyflichub/client.py +++ b/pyflichub/client.py @@ -2,6 +2,7 @@ import json import logging import time +from datetime import datetime from functools import partial, wraps from typing import Union @@ -11,6 +12,7 @@ from pyflichub.command import Command from pyflichub.event import Event from pyflichub.button import FlicButton +from pyflichub.network import Network _LOGGER = logging.getLogger(__name__) @@ -30,6 +32,7 @@ async def run(*args, loop=None, executor=None, **kwargs): class FlicHubTcpClient(asyncio.Protocol): buttons: [FlicButton] = [] + network: Network def __init__(self, ip, port, loop, timeout=1.0, reconnect_timeout=10.0, event_callback=None, command_callback=None): self._data_ready: Union[asyncio.Event, None] = None @@ -89,6 +92,9 @@ async def connect(self): async def get_buttons(self): return await self._async_send_command('buttons') + async def get_network(self): + return await self._async_send_command('network') + async def get_battery_status(self, bdaddr: str): return await self._async_send_command(f'battery;{bdaddr}') @@ -113,18 +119,19 @@ def connection_made(self, transport): def data_received(self, data): decoded_data = data.decode() _LOGGER.debug('Data received: {!r}'.format(decoded_data)) + for data_part in [data_part for data_part in decoded_data.split("\n") if data_part.strip()]: + if data_part == 'pong': + pass - if decoded_data == 'pong': - return - - try: - msg = json.loads(decoded_data) - if 'event' in msg: - self._handle_event(Event(**msg)) - if 'command' in msg: - self._handle_command(Command(**msg)) - except Exception: - _LOGGER.warning('Unable to decode received data') + try: + msg = json.loads(data_part, cls=_JSONDecoder) + if 'event' in msg: + self._handle_event(Event(**msg)) + if 'command' in msg: + self._handle_command(Command(**msg)) + except Exception as e: + _LOGGER.warning(e, exc_info = True) + _LOGGER.warning('Unable to decode received data') def connection_lost(self, exc): @@ -139,6 +146,13 @@ def _handle_command(self, cmd: Command): command_data = cmd.data = self.buttons for button in self.buttons: _LOGGER.debug(f"Button name: {button.name} - Connected: {button.connected}") + if cmd.command == 'network': + self.network = Network(**humps.decamelize(cmd.data)) + command_data = cmd.data = self.network + if self.network.has_wifi(): + _LOGGER.debug(f"Wifi State: {self.network.wifi.state} - Connected: {self.network.wifi.connected}") + if self.network.has_ethernet(): + _LOGGER.debug(f"Ethernet IP: {self.network.ethernet.ip} - Connected: {self.network.ethernet.connected}") if self._data_ready is not None: self._data_ready.set() @@ -170,3 +184,17 @@ def _check_connection(self): msg = "" self._transport.write(msg.encode()) self._tcp_check_timer = time.time() + +class _JSONDecoder(json.JSONDecoder): + def __init__(self, *args, **kwargs): + json.JSONDecoder.__init__( + self, object_hook=self.object_hook, *args, **kwargs) + + def object_hook(self, obj): + ret = {} + for key, value in obj.items(): + if key in {'batteryTimestamp'}: + ret[key] = datetime.fromtimestamp(value/1000) + else: + ret[key] = value + return ret \ No newline at end of file diff --git a/pyflichub/network.py b/pyflichub/network.py new file mode 100644 index 0000000..2c5d61c --- /dev/null +++ b/pyflichub/network.py @@ -0,0 +1,56 @@ +from typing import List + + +class WifiInfo: + state: str + ssid: str + + def __init__(self, connected, ip, mac): + self.connected = connected + self.ip = ip + self.mac = mac + + +class EthernetInfo: + def __init__(self, connected, ip, mac): + self.connected = connected + self.ip = ip + self.mac = mac + + +class DhcpInfo: + def __init__(self, wifi=None, ethernet=None): + self.wifi = WifiInfo(**wifi) if wifi else None + self.ethernet = EthernetInfo(**ethernet) if ethernet else None + + +class _WifiState: + def __init__(self, state, ssid): + self.state = state + self.ssid = ssid + + +def _decode_ssid(ssid_list): + return ''.join(chr(byte) for byte in ssid_list) + + +class Network: + def __init__(self, dhcp, wifi_state=None): + self._dhcp = DhcpInfo(**dhcp) + self._dhcp.wifi.state = wifi_state.get('state', None) if wifi_state else None + self._dhcp.wifi.ssid = _decode_ssid(wifi_state.get('ssid', None)) if wifi_state else None + + def has_wifi(self) -> bool: + return self._dhcp.wifi is not None + + def has_ethernet(self) -> bool: + return self._dhcp.ethernet is not None + + @property + def wifi(self) -> WifiInfo: + return self._dhcp.wifi + + @property + def ethernet(self) -> EthernetInfo: + return self._dhcp.ethernet + diff --git a/tcpserver.js b/tcpserver.js index b6e0584..84f2b9e 100644 --- a/tcpserver.js +++ b/tcpserver.js @@ -1,7 +1,9 @@ console.log("Made by JohNan - https://github.com/JohNan/pyflichub-tcpclient") +var network = require('network'); var net = require('net'); var buttons = require('buttons'); +const EOL = "\n"; // Configuration - start const PORT = 8124; @@ -20,7 +22,7 @@ net.createServer(function (socket) { 'button': button.bdaddr, 'action': '' } - socket.write(JSON.stringify(response)) + socket.write(JSON.stringify(response)+EOL) }; @@ -31,7 +33,7 @@ net.createServer(function (socket) { 'button': button.bdaddr, 'action': '' } - socket.write(JSON.stringify(response)) + socket.write(JSON.stringify(response)+EOL) }; var buttonAddedHandler = function (button) { @@ -41,7 +43,7 @@ net.createServer(function (socket) { 'button': button.bdaddr, 'action': '' } - socket.write(JSON.stringify(response)) + socket.write(JSON.stringify(response)+EOL) }; var buttonDownHandler = function (button) { @@ -51,7 +53,7 @@ net.createServer(function (socket) { 'button': button.bdaddr, 'action': 'down' } - socket.write(JSON.stringify(response)) + socket.write(JSON.stringify(response)+EOL) }; var buttonUpHandler = function (button) { @@ -61,7 +63,7 @@ net.createServer(function (socket) { 'button': button.bdaddr, 'action': 'up' } - socket.write(JSON.stringify(response)) + socket.write(JSON.stringify(response)+EOL) }; var buttonSingleOrDoubleClickOrHoldHandler = function (button) { @@ -72,7 +74,7 @@ net.createServer(function (socket) { 'button': button.bdaddr, 'action': action }; - socket.write(JSON.stringify(response)) + socket.write(JSON.stringify(response)+EOL) }; function sendButtons() { @@ -84,7 +86,8 @@ net.createServer(function (socket) { 'data': _buttons }; - socket.write(JSON.stringify(response)) + console.log(response) + socket.write(JSON.stringify(response)+EOL) } buttons.on('buttonSingleOrDoubleClickOrHold', buttonSingleOrDoubleClickOrHoldHandler); @@ -123,7 +126,7 @@ net.createServer(function (socket) { 'data': button.batteryStatus }; - socket.write(JSON.stringify(response)) + socket.write(JSON.stringify(response)+EOL) } if (msg === "buttons") { @@ -135,21 +138,28 @@ net.createServer(function (socket) { 'data': _buttons }; - socket.write(JSON.stringify(response)) + socket.write(JSON.stringify(response)+EOL) + } + + if (msg === "network") { + const _network = network.getState(); + console.log(JSON.stringify(_network)) + + const response = { + 'command': 'network', + 'data': _network + }; + + socket.write(JSON.stringify(response)+EOL) } if (msg === "ping") { socket.write("pong") } }); - - // Send current buttons every 10 seconds - refreshIntervalId = setInterval(sendButtons, 10000); }).listen(PORT, function () { console.log("Opened server on port: " + PORT); }); console.log("The server should have started now!") -console.log("Waiting for connections...") - - +console.log("Waiting for connections...") \ No newline at end of file