Merge branch 'main' into mqtt_lwt
This commit is contained in:
16
HCDevice.py
16
HCDevice.py
@@ -93,7 +93,8 @@ class HCDevice:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
# Based on PR submitted https://github.com/Skons/hcpy/pull/1
|
# Based on PR submitted https://github.com/Skons/hcpy/pull/1
|
||||||
def test_program_data(self, data):
|
def test_program_data(self, data_array):
|
||||||
|
for data in data_array:
|
||||||
if "program" not in data:
|
if "program" not in data:
|
||||||
raise TypeError("Message data invalid, no program specified.")
|
raise TypeError("Message data invalid, no program specified.")
|
||||||
|
|
||||||
@@ -128,7 +129,8 @@ class HCDevice:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Test the feature of an appliance agains a data object
|
# Test the feature of an appliance agains a data object
|
||||||
def test_feature(self, data):
|
def test_feature(self, data_array):
|
||||||
|
for data in data_array:
|
||||||
if "uid" not in data:
|
if "uid" not in data:
|
||||||
raise Exception("Unable to configure appliance. UID is required.")
|
raise Exception("Unable to configure appliance. UID is required.")
|
||||||
|
|
||||||
@@ -164,9 +166,10 @@ class HCDevice:
|
|||||||
if "values" in feature:
|
if "values" in feature:
|
||||||
if isinstance(data["value"], int) is False:
|
if isinstance(data["value"], int) is False:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
f"Unable to configure appliance. The value {data['value']} must be an integer."
|
f"Unable to configure appliance. The value {data['value']} must "
|
||||||
f" Allowed values are {feature['values']}."
|
f"be an integer. Allowed values are {feature['values']}."
|
||||||
)
|
)
|
||||||
|
|
||||||
value = str(data["value"])
|
value = str(data["value"])
|
||||||
# values are strings in the feature list,
|
# values are strings in the feature list,
|
||||||
# but always seem to be an integer. An integer must be provided
|
# but always seem to be an integer. An integer must be provided
|
||||||
@@ -230,6 +233,9 @@ class HCDevice:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if data is not None:
|
if data is not None:
|
||||||
|
if isinstance(data, list) is False:
|
||||||
|
data = [data]
|
||||||
|
|
||||||
if action == "POST":
|
if action == "POST":
|
||||||
if resource == "/ro/values":
|
if resource == "/ro/values":
|
||||||
# Raises exceptions on failure
|
# Raises exceptions on failure
|
||||||
@@ -238,7 +244,7 @@ class HCDevice:
|
|||||||
# Raises exception on failure
|
# Raises exception on failure
|
||||||
self.test_program_data(data)
|
self.test_program_data(data)
|
||||||
|
|
||||||
msg["data"] = [data]
|
msg["data"] = data
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.ws.send(msg)
|
self.ws.send(msg)
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
# Home Assistant
|
|
||||||
|
|
||||||
For integration with Home Assistant, the following MQTT examples can be used to create entities:
|
|
||||||
|
|
||||||
## Coffee Machine
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- unique_id: "coffee_machine"
|
|
||||||
name: "Coffee Machine"
|
|
||||||
state_topic: "homeconnect/coffeemaker/state"
|
|
||||||
value_template: "{{ value_json.PowerState }}"
|
|
||||||
json_attributes_topic: "homeconnect/coffeemaker/state"
|
|
||||||
json_attributes_template: "{{ value_json | tojson }}"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Extractor Fan
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
fan:
|
|
||||||
- name: "Hood"
|
|
||||||
state_topic: "homeconnect/hood/state"
|
|
||||||
state_value_template: "{{ value_json.PowerState }}"
|
|
||||||
command_topic: "homeconnect/hood/set"
|
|
||||||
command_template: "{{ iif(value == 'On', '{\"uid\":539,\"value\":2}', '{\"uid\":539,\"value\":1}') }}"
|
|
||||||
payload_on: "On"
|
|
||||||
payload_off: "Off"
|
|
||||||
light:
|
|
||||||
- name: "Hood Work Light" # We can only turn the light on, but not off as no command_template
|
|
||||||
state_topic: "homeconnect/hood/state"
|
|
||||||
state_value_template: "{{ value_json.Lighting }}"
|
|
||||||
brightness_state_topic: "homeconnect/hood/state"
|
|
||||||
brightness_value_template: "{{ value_json.LightingBrightness }}"
|
|
||||||
brightness_command_topic: "homeconnect/hood/set"
|
|
||||||
brightness_command_template: "{{ '{\"uid\":53254,\"value\":' + value|string + '}' }}"
|
|
||||||
brightness_scale: 100
|
|
||||||
command_topic: "homeconnect/hood/set"
|
|
||||||
on_command_type: brightness
|
|
||||||
#command_template: "{{ iif(value == 'on', '{\"uid\":53253,\"value\":true}', '{\"uid\":53253,\"value\":false}') }}" WIP - MQTT doesn't allow this to be configured
|
|
||||||
payload_on: true
|
|
||||||
payload_off: false
|
|
||||||
```
|
|
||||||
|
|
||||||
## Refrigerator/Freezers
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
binary_sensor:
|
|
||||||
- name: "Freezer Door"
|
|
||||||
state_topic: "homeconnect/freezer/state"
|
|
||||||
value_template: "{{ value_json.Freezer }}"
|
|
||||||
payload_on: "Open"
|
|
||||||
payload_off: "Closed"
|
|
||||||
device_class: door
|
|
||||||
json_attributes_topic: "homeconnect/freezer/state"
|
|
||||||
- name: "Fridge Door"
|
|
||||||
state_topic: "homeconnect/refrigerator/state"
|
|
||||||
value_template: "{{ value_json.DoorState }}" # Also Refrigerator
|
|
||||||
payload_on: "Open"
|
|
||||||
payload_off: "Closed"
|
|
||||||
device_class: door
|
|
||||||
json_attributes_topic: "homeconnect/refrigerator/state"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Dishwasher
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- name: "Dishwasher"
|
|
||||||
state_topic: "homeconnect/dishwasher/state"
|
|
||||||
value_template: "{{ value_json.PowerState }}"
|
|
||||||
payload_on: "On"
|
|
||||||
payload_off: "Off"
|
|
||||||
json_attributes_topic: "homeconnect/dishwasher/state"
|
|
||||||
- name: "Dishwasher Door"
|
|
||||||
state_topic: "homeconnect/dishwasher/state"
|
|
||||||
value_template: "{{ value_json.DoorState }}"
|
|
||||||
payload_on: "Open"
|
|
||||||
payload_off: "Closed"
|
|
||||||
device_class: door
|
|
||||||
```
|
|
||||||
|
|
||||||
123
README-frida.md
123
README-frida.md
@@ -1,123 +0,0 @@
|
|||||||
This is for historical info; it is no longer necessary
|
|
||||||
unless you're trying to make sense of the phone application
|
|
||||||
or XML code.
|
|
||||||
|
|
||||||
## Finding the PSK and IV (no longer necessary)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
You will need to set the dishwasher to "`Local network only`"
|
|
||||||
in the setup application so that your phone will connect
|
|
||||||
directly to it, rather than going through the cloud services.
|
|
||||||
|
|
||||||
You'll also need a rooted Android phone running `frida-server`
|
|
||||||
and the `find-psk.frida` script. This will hook the callback
|
|
||||||
from the OpenSSL library `hcp::client_psk_callback` that is called
|
|
||||||
when OpenSSL has made a connection and now needs to establish
|
|
||||||
the PSK.
|
|
||||||
|
|
||||||
```
|
|
||||||
frida --no-pause -f com.bshg.homeconnect.android.release -U -l find-psk.frida
|
|
||||||
```
|
|
||||||
|
|
||||||
It should start the Home Connect application and eventually
|
|
||||||
print a message like:
|
|
||||||
|
|
||||||
```
|
|
||||||
psk callback hint 'HCCOM_Local_App'
|
|
||||||
psk 32 0x6ee63fb2f0
|
|
||||||
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
|
|
||||||
00000000 0e c8 1f d8 c6 49 fa d8 bc e7 fd 34 33 54 13 d4 .....I.....43T..
|
|
||||||
00000010 73 f9 2e 01 fc d8 26 80 49 89 4c 19 d7 2e cd cb s.....&.I.L.....
|
|
||||||
```
|
|
||||||
|
|
||||||
Which gives you the 32-byte PSK value to copy into the `hcpy` program.
|
|
||||||
|
|
||||||
## SSL logging
|
|
||||||
|
|
||||||
The Frida script will also dump all of the SSL traffic so that you can
|
|
||||||
see different endpoints and things. Not much is documented yet.
|
|
||||||
|
|
||||||
Note that the TX from the phone on the websocket is "masked" with an
|
|
||||||
repeating 4-byte XOR that is sent in the first part of each messages.
|
|
||||||
The script could be augmented to decode those as well.
|
|
||||||
The replies from the device are not masked so they can be read in the clear.
|
|
||||||
|
|
||||||
## Retrieving home appliance configuration
|
|
||||||
|
|
||||||
```
|
|
||||||
frida-trace -o initHomeAppliance.log -f "com.bshg.homeconnect.android.release" -U -j '*!initHomeAppliance''
|
|
||||||
```
|
|
||||||
|
|
||||||
PSK can also be found in the last section of the config as base64url encoded.
|
|
||||||
|
|
||||||
```
|
|
||||||
echo 'Dsgf2MZJ-ti85_00M1QT1HP5LgH82CaASYlMGdcuzcs"' | tr '_\-"' '/+=' | base64 -d | xxd -g1
|
|
||||||
```
|
|
||||||
|
|
||||||
The IV is also there for devices that use it. This needs better documentation.
|
|
||||||
|
|
||||||
TODO: document the other frida scripts that do `sendmsg()` and `Encrypt()` / `Decrypt()` tracing
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## hcpy
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
The `hcpy` tool can contact your device, and if the PSK is correct, it will
|
|
||||||
register for notification of events.
|
|
||||||
|
|
||||||
```
|
|
||||||
RX: {'sID': 2354590730, 'msgID': 3734589701, 'resource': '/ei/initialValues', 'version': 2, 'action': 'POST', 'data': [{'edMsgID': 3182729968}]}
|
|
||||||
TX: {"sID":2354590730,"msgID":3734589701,"resource":"/ei/initialValues","version":2,"action":"RESPONSE","data":[{"deviceType":"Application","deviceName":"py-hca","deviceID":"1234"}]}
|
|
||||||
TX: {"sID":2354590730,"msgID":3182729968,"resource":"/ci/services","version":1,"action":"GET"}
|
|
||||||
TX: {"sID":2354590730,"msgID":3182729969,"resource":"/iz/info","version":1,"action":"GET"}
|
|
||||||
TX: {"sID":2354590730,"msgID":3182729970,"resource":"/ei/deviceReady","version":2,"action":"NOTIFY"}
|
|
||||||
RX: {'sID': 2354590730, 'msgID': 3182729968, 'resource': '/ci/services', 'version': 1, 'action': 'RESPONSE', 'data': [{'service': 'ci', 'version': 3}, {'service': 'ei', 'version': 2}, {'service': 'iz', 'version': 1}, {'service': 'ni', 'version': 1}, {'service': 'ro', 'version': 1}]}
|
|
||||||
RX: {'sID': 2354590730, 'msgID': 3182729969, 'resource': '/iz/info', 'version': 1, 'action': 'RESPONSE', 'data': [{'deviceID': '....', 'eNumber': 'SX65EX56CN/11', 'brand': 'SIEMENS', 'vib': 'SX65EX56CN', 'mac': '....', 'haVersion': '1.4', 'swVersion': '3.2.10.20200911163726', 'hwVersion': '2.0.0.2', 'deviceType': 'Dishwasher', 'deviceInfo': '', 'customerIndex': '11', 'serialNumber': '....', 'fdString': '0201', 'shipSki': '....'}]}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Feature UID mapping
|
|
||||||
|
|
||||||
There are other things that can be hooked in the application
|
|
||||||
to get the mappings of the `uid` to actual menu settings and
|
|
||||||
XML files of the configuration parameters.
|
|
||||||
|
|
||||||
In the `xml/` directory are some of the device descriptions
|
|
||||||
and feature maps that the app downloads from the Home Connect
|
|
||||||
servers. Note that the XML has unadorned hex, while the
|
|
||||||
websocket messages are in decimal.
|
|
||||||
|
|
||||||
For instance, when the dishwasher door is closed and then
|
|
||||||
re-opened, it sends the messages for `'uid':512`, which is 0x020F hex:
|
|
||||||
|
|
||||||
```
|
|
||||||
RX: {... 'data': [{'uid': 527, 'value': 1}]}
|
|
||||||
RX: {... 'data': [{'uid': 527, 'value': 0}]}
|
|
||||||
```
|
|
||||||
|
|
||||||
In the `xml/dishwasher-description.xml` there is a `statusList`
|
|
||||||
that says uid 0x020f is a readonly value that uses enum 0x0201:
|
|
||||||
|
|
||||||
```
|
|
||||||
<status access="read" available="true" enumerationType="0201" refCID="03" refDID="80" uid="020F"/>
|
|
||||||
```
|
|
||||||
|
|
||||||
In the `xml/dishwasher-featuremap.xml` there is a mapping of feature
|
|
||||||
reference UIDs to names:
|
|
||||||
|
|
||||||
```
|
|
||||||
<feature refUID="020F">BSH.Common.Status.DoorState</feature>
|
|
||||||
```
|
|
||||||
|
|
||||||
as well as mappings of enum ids to enum names and values:
|
|
||||||
|
|
||||||
```
|
|
||||||
<enumDescription enumKey="BSH.Common.EnumType.DoorState" refENID="0201">
|
|
||||||
<enumMember refValue="0">Open</enumMember>
|
|
||||||
<enumMember refValue="1">Closed</enumMember>
|
|
||||||
</enumDescription>
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user