Skip to content

Commit

Permalink
Allow defining device_id for push server (#1710)
Browse files Browse the repository at this point in the history
This is necessary to allow defining the did for simulators,
the device id is a better unique id than the mac address because it does
not require working `miIO.info` method:
1. it is available in mdns names
2. it is available in the handshake responses

Renames server_id to device_id to use consistent naming
  • Loading branch information
rytilahti authored Feb 3, 2023
1 parent c656903 commit bf02f1b
Show file tree
Hide file tree
Showing 6 changed files with 25 additions and 25 deletions.
10 changes: 5 additions & 5 deletions miio/devtools/simulators/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ def create_info_response(model, addr, mac):
return INFO_RESPONSE


def mac_from_model(model):
"""Creates a mac address based on the model name.
def did_and_mac_for_model(model):
"""Creates a device id and a mac address based on the model name.
This allows simulating multiple different devices separately as the homeassistant
unique_id is based on the mac address.
These identifiers allow making a simulated device unique for testing.
"""
m = md5() # nosec
m.update(model.encode())
digest = m.hexdigest()[:12]
did = int(digest[:8], base=16)
mac = ":".join([digest[i : i + 2] for i in range(0, len(digest), 2)])
return mac
return did, mac
8 changes: 4 additions & 4 deletions miio/devtools/simulators/miiosimulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from miio import PushServer

from .common import create_info_response, mac_from_model
from .common import create_info_response, did_and_mac_for_model

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -135,13 +135,13 @@ def handle_set(self, payload):


async def main(dev):
server = PushServer()
did, mac = did_and_mac_for_model(dev)
server = PushServer(device_id=did)

_ = MiioSimulator(dev=dev, server=server)
mac = mac_from_model(dev._model)
server.add_method("miIO.info", create_info_response(dev._model, "127.0.0.1", mac))

transport, proto = await server.start()
await server.start()


@click.command()
Expand Down
7 changes: 3 additions & 4 deletions miio/devtools/simulators/miotsimulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from miio.miot_cloud import MiotCloud
from miio.miot_models import DeviceModel, MiotAccess, MiotProperty, MiotService

from .common import create_info_response, mac_from_model
from .common import create_info_response, did_and_mac_for_model

_LOGGER = logging.getLogger(__name__)
UNSET = -10000
Expand Down Expand Up @@ -248,9 +248,8 @@ def action(self, payload):


async def main(dev, model):
server = PushServer()

mac = mac_from_model(model)
device_id, mac = did_and_mac_for_model(model)
server = PushServer(device_id=device_id)
simulator = MiotSimulator(device_model=dev)
server.add_method("miIO.info", create_info_response(model, "127.0.0.1", mac))
server.add_method("action", simulator.action)
Expand Down
11 changes: 6 additions & 5 deletions miio/push_server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,14 @@ class PushServer:
await push_server.stop()
"""

def __init__(self, device_ip=None):
def __init__(self, *, device_ip=None, device_id=None):
"""Initialize the class."""
self._device_ip = device_ip

self._address = "0.0.0.0" # nosec
self._server_ip = None
self._server_id = int(FAKE_DEVICE_ID)

self._device_id = device_id if device_id is not None else int(FAKE_DEVICE_ID)
self._server_model = FAKE_DEVICE_MODEL

self._loop = None
Expand Down Expand Up @@ -282,7 +283,7 @@ def _construct_event( # nosec

target_data = {
"command": command,
"did": str(self.server_id),
"did": str(self.device_id),
"extra": info.command_extra,
"id": message_id,
"ip": self.server_ip,
Expand Down Expand Up @@ -316,9 +317,9 @@ def server_ip(self):
return self._server_ip

@property
def server_id(self):
def device_id(self):
"""Return the ID of the fake device beeing emulated."""
return self._server_id
return self._device_id

@property
def server_model(self):
Expand Down
8 changes: 4 additions & 4 deletions miio/push_server/serverprotocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def _build_ack(self):
timestamp = calendar.timegm(datetime.datetime.now().timetuple())
# ACK packet not signed, 16 bytes header + 16 bytes of zeroes
return struct.pack(
">HHIII16s", 0x2131, 32, 0, self.server.server_id, timestamp, bytes(16)
">HHIII16s", 0x2131, 32, 0, self.server.device_id, timestamp, bytes(16)
)

def connection_made(self, transport):
Expand All @@ -42,7 +42,7 @@ def connection_made(self, transport):
_LOGGER.info(
"Miio push server started with address=%s server_id=%s",
self.server._address,
self.server.server_id,
self.server.device_id,
)

def connection_lost(self, exc):
Expand All @@ -54,7 +54,7 @@ def send_ping_ACK(self, host, port):
_LOGGER.debug("%s:%s=>PING", host, port)
m = self._build_ack()
self.transport.sendto(m, (host, port))
_LOGGER.debug("%s:%s<=ACK(server_id=%s)", host, port, self.server.server_id)
_LOGGER.debug("%s:%s<=ACK(server_id=%s)", host, port, self.server.device_id)

def _create_message(self, data, token, device_id):
"""Create a message to be sent to the client."""
Expand All @@ -78,7 +78,7 @@ def send_response(self, host, port, msg_id, token, payload=None):
payload = {}

data = {**payload, "id": msg_id}
msg = self._create_message(data, token, device_id=self.server.server_id)
msg = self._create_message(data, token, device_id=self.server.device_id)

self.transport.sendto(msg, (host, port))
_LOGGER.debug(">> %s:%s: %s", host, port, data)
Expand Down
6 changes: 3 additions & 3 deletions miio/push_server/test_serverprotocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

HOST = "127.0.0.1"
PORT = 1234
SERVER_ID = 4141
DEVICE_ID = 4141
DUMMY_TOKEN = bytes.fromhex("0" * 32)


Expand All @@ -20,7 +20,7 @@ def protocol(mocker, event_loop) -> ServerProtocol:
server = mocker.Mock()

# Mock server id
type(server).server_id = mocker.PropertyMock(return_value=SERVER_ID)
type(server).device_id = mocker.PropertyMock(return_value=DEVICE_ID)
socket = mocker.Mock()

proto = ServerProtocol(event_loop, socket, server)
Expand All @@ -37,7 +37,7 @@ def test_send_ping_ack(protocol: ServerProtocol, mocker):
cargs = protocol.transport.sendto.call_args[0]

m = Message.parse(cargs[0])
assert int.from_bytes(m.header.value.device_id, "big") == SERVER_ID
assert int.from_bytes(m.header.value.device_id, "big") == DEVICE_ID
assert m.data.length == 0

assert cargs[1][0] == HOST
Expand Down

0 comments on commit bf02f1b

Please sign in to comment.