diff --git a/.gitignore b/.gitignore index f40f208f..5a789c8 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ __snapshots__ !custom_components/ramses_cc !custom_components/ramses_cc/manifest.json +!custom_components/ramses_cc/services.yaml !.flake8 !.gitignore diff --git a/custom_components/ramses_cc/schemas.py b/custom_components/ramses_cc/schemas.py index 8e4389a..fd2ec43 100644 --- a/custom_components/ramses_cc/schemas.py +++ b/custom_components/ramses_cc/schemas.py @@ -231,7 +231,8 @@ def schema_is_minimal(schema: _SchemaT) -> bool: SCH_SEND_PACKET = vol.Schema( { - vol.Required(ATTR_DEVICE_ID): cv.matches_regex(r"^[0-9]{2}:[0-9]{6}$"), + vol.Required(ATTR_DEVICE_ID): _SCH_DEVICE_ID, + vol.Optional("from_id"): _SCH_DEVICE_ID, vol.Required("verb"): vol.In((" I", "I", "RQ", "RP", " W", "W")), vol.Required("code"): cv.matches_regex(r"^[0-9A-F]{4}$"), vol.Required("payload"): cv.matches_regex(r"^([0-9A-F][0-9A-F]){1,48}$"), diff --git a/custom_components/ramses_cc/services.yaml b/custom_components/ramses_cc/services.yaml index ebc35b8..c782c1b 100644 --- a/custom_components/ramses_cc/services.yaml +++ b/custom_components/ramses_cc/services.yaml @@ -65,6 +65,14 @@ send_packet: example: 01:123456 required: true + from_id: + description: >- + The source device ID (a RAMSES ID, not an entity_id). + This can be used to send a packet from a faked device. + Optional: if not specified, the device ID of the gateway is used. + example: 18:123456 + required: false + verb: description: 'The packet verb, one of: I, RQ, RP, W (leading space not required).' example: RQ diff --git a/custom_components/ramses_cc/translations/en.json b/custom_components/ramses_cc/translations/en.json index 01c798c..fd182f2 100644 --- a/custom_components/ramses_cc/translations/en.json +++ b/custom_components/ramses_cc/translations/en.json @@ -152,7 +152,7 @@ "advanced_features": { "title": "Advanced features", "data": { - "send_packet": "Enable send_packet service for broadcasting bespoke packets", + "send_packet": "Enable send_packet service for casting bespoke packets", "message_events": "Emit events for messages matching regular expression" }, "data_description": { diff --git a/tests/tests_old/test_services.py b/tests/tests_old/test_services.py index fac0c2c..ddcedd1 100644 --- a/tests/tests_old/test_services.py +++ b/tests/tests_old/test_services.py @@ -99,6 +99,7 @@ "03:123456": {"class": "THM", "faked": True}, "32:097710": {"class": "CO2"}, "32:139773": {"class": "HUM"}, + "37:123456": {"class": "FAN"}, "40:123456": {"class": "REM", "faked": True}, }, } @@ -793,3 +794,20 @@ async def test_svc_send_packet(hass: HomeAssistant, entry: ConfigEntry) -> None: schemas = {SVC_SEND_PACKET: SCH_SEND_PACKET} await _test_service_call(hass, SVC_SEND_PACKET, data, schemas=schemas) + + +async def test_svc_send_packet_with_impersonation( + hass: HomeAssistant, entry: ConfigEntry +) -> None: + """Test the service call.""" + + data = { + "device_id": "37:123456", + "from_id": "40:123456", + "verb": " I", + "code": "22F1", + "payload": "000304", + } + schemas = {SVC_SEND_PACKET: SCH_SEND_PACKET} + + await _test_service_call(hass, SVC_SEND_PACKET, data, schemas=schemas)