Skip to content

Commit d431c2d

Browse files
author
Ted Roberts
committed
Add raw commands service (v0.2.13)
- Add 'Allow Raw Commands' config option (off by default) - Add novastar_h.send_raw_command service for raw API access - Service requires host, endpoint, and JSON body parameters
1 parent aeada32 commit d431c2d

8 files changed

Lines changed: 162 additions & 6 deletions

File tree

custom_components/novastar_h/__init__.py

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@
33
import logging
44
from typing import Any
55

6+
import voluptuous as vol
67
from homeassistant.config_entries import ConfigEntry
78
from homeassistant.const import CONF_HOST, CONF_PORT
8-
from homeassistant.core import HomeAssistant
9+
from homeassistant.core import HomeAssistant, ServiceCall
10+
import homeassistant.helpers.config_validation as cv
911

1012
from .api import NovastarClient
1113
from .const import (
14+
CONF_ALLOW_RAW_COMMANDS,
1215
CONF_DEVICE_ID,
1316
CONF_ENCRYPTION,
1417
CONF_PROJECT_ID,
1518
CONF_SCREEN_ID,
1619
CONF_SECRET_KEY,
20+
DEFAULT_ALLOW_RAW_COMMANDS,
1721
DEFAULT_DEVICE_ID,
1822
DEFAULT_ENCRYPTION,
1923
DEFAULT_PORT,
@@ -26,6 +30,18 @@
2630

2731
_LOGGER = logging.getLogger(__name__)
2832

33+
SERVICE_SEND_RAW_COMMAND = "send_raw_command"
34+
ATTR_ENDPOINT = "endpoint"
35+
ATTR_BODY = "body"
36+
37+
SERVICE_SEND_RAW_COMMAND_SCHEMA = vol.Schema(
38+
{
39+
vol.Required(CONF_HOST): cv.string,
40+
vol.Required(ATTR_ENDPOINT): cv.string,
41+
vol.Required(ATTR_BODY): dict,
42+
}
43+
)
44+
2945

3046
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
3147
"""Set up Novastar H Series from a config entry."""
@@ -56,6 +72,47 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
5672
"device_info": device_info,
5773
}
5874

75+
# Register send_raw_command service if enabled and not already registered
76+
if entry.data.get(CONF_ALLOW_RAW_COMMANDS, DEFAULT_ALLOW_RAW_COMMANDS):
77+
if not hass.services.has_service(DOMAIN, SERVICE_SEND_RAW_COMMAND):
78+
async def async_send_raw_command(call: ServiceCall) -> None:
79+
"""Handle send_raw_command service call."""
80+
host = call.data[CONF_HOST]
81+
endpoint = call.data[ATTR_ENDPOINT]
82+
body = call.data[ATTR_BODY]
83+
84+
# Find the client for the specified host
85+
client_found = None
86+
for eid, data in hass.data[DOMAIN].items():
87+
if isinstance(data, dict) and "client" in data:
88+
if data["client"].host == host:
89+
# Check if this entry allows raw commands
90+
config_entry = hass.config_entries.async_get_entry(eid)
91+
if config_entry and config_entry.data.get(
92+
CONF_ALLOW_RAW_COMMANDS, DEFAULT_ALLOW_RAW_COMMANDS
93+
):
94+
client_found = data["client"]
95+
break
96+
97+
if not client_found:
98+
_LOGGER.error(
99+
"No Novastar device found at %s with raw commands enabled", host
100+
)
101+
return
102+
103+
result = await client_found.async_send_raw_command(endpoint, body)
104+
if result is None:
105+
_LOGGER.warning("Raw command to %s failed", endpoint)
106+
else:
107+
_LOGGER.debug("Raw command result: %s", result)
108+
109+
hass.services.async_register(
110+
DOMAIN,
111+
SERVICE_SEND_RAW_COMMAND,
112+
async_send_raw_command,
113+
schema=SERVICE_SEND_RAW_COMMAND_SCHEMA,
114+
)
115+
59116
loaded_platforms: list[Any] = []
60117
for platform in PLATFORMS:
61118
try:

custom_components/novastar_h/api.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,3 +444,17 @@ async def async_get_device_status_info(
444444
if signal_status is not None and isinstance(signal_status, (int, float)):
445445
result["signal_status"] = int(signal_status)
446446
return result
447+
448+
async def async_send_raw_command(
449+
self, endpoint: str, body: dict[str, Any]
450+
) -> dict[str, Any] | None:
451+
"""Send a raw API command.
452+
453+
Args:
454+
endpoint: API endpoint path (e.g., "device/readDetail")
455+
body: Business data for the request body
456+
457+
Returns:
458+
Response body dict on success, None on failure
459+
"""
460+
return await self._async_request(endpoint, body)

custom_components/novastar_h/config_flow.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@
1111

1212
from .api import NovastarClient
1313
from .const import (
14+
CONF_ALLOW_RAW_COMMANDS,
1415
CONF_ENCRYPTION,
1516
CONF_PROJECT_ID,
1617
CONF_SECRET_KEY,
18+
DEFAULT_ALLOW_RAW_COMMANDS,
1719
DEFAULT_ENCRYPTION,
1820
DEFAULT_NAME,
1921
DEFAULT_PORT,
@@ -49,6 +51,7 @@ async def async_step_user(
4951
project_id = user_input[CONF_PROJECT_ID]
5052
secret_key = user_input[CONF_SECRET_KEY]
5153
encryption = user_input.get(CONF_ENCRYPTION, DEFAULT_ENCRYPTION)
54+
allow_raw_commands = user_input.get(CONF_ALLOW_RAW_COMMANDS, DEFAULT_ALLOW_RAW_COMMANDS)
5255

5356
# Validate credentials by testing connection
5457
client = NovastarClient(
@@ -71,6 +74,7 @@ async def async_step_user(
7174
CONF_PROJECT_ID: project_id,
7275
CONF_SECRET_KEY: secret_key,
7376
CONF_ENCRYPTION: encryption,
77+
CONF_ALLOW_RAW_COMMANDS: allow_raw_commands,
7478
},
7579
)
7680
errors["base"] = "cannot_connect"
@@ -84,6 +88,7 @@ async def async_step_user(
8488
vol.Required(CONF_PROJECT_ID): str,
8589
vol.Required(CONF_SECRET_KEY): str,
8690
vol.Optional(CONF_ENCRYPTION, default=DEFAULT_ENCRYPTION): bool,
91+
vol.Optional(CONF_ALLOW_RAW_COMMANDS, default=DEFAULT_ALLOW_RAW_COMMANDS): bool,
8792
vol.Optional(CONF_NAME, default=DEFAULT_NAME): str,
8893
}
8994
),
@@ -151,6 +156,7 @@ async def async_step_manual(
151156
project_id = user_input[CONF_PROJECT_ID]
152157
secret_key = user_input[CONF_SECRET_KEY]
153158
encryption = user_input.get(CONF_ENCRYPTION, DEFAULT_ENCRYPTION)
159+
allow_raw_commands = user_input.get(CONF_ALLOW_RAW_COMMANDS, DEFAULT_ALLOW_RAW_COMMANDS)
154160

155161
# Validate credentials by testing connection
156162
client = NovastarClient(
@@ -173,6 +179,7 @@ async def async_step_manual(
173179
CONF_PROJECT_ID: project_id,
174180
CONF_SECRET_KEY: secret_key,
175181
CONF_ENCRYPTION: encryption,
182+
CONF_ALLOW_RAW_COMMANDS: allow_raw_commands,
176183
},
177184
)
178185
errors["base"] = "cannot_connect"
@@ -186,6 +193,7 @@ async def async_step_manual(
186193
vol.Required(CONF_PROJECT_ID): str,
187194
vol.Required(CONF_SECRET_KEY): str,
188195
vol.Optional(CONF_ENCRYPTION, default=DEFAULT_ENCRYPTION): bool,
196+
vol.Optional(CONF_ALLOW_RAW_COMMANDS, default=DEFAULT_ALLOW_RAW_COMMANDS): bool,
189197
vol.Optional(CONF_NAME, default=DEFAULT_NAME): str,
190198
}
191199
),
@@ -225,6 +233,7 @@ async def async_step_credentials(
225233
CONF_PROJECT_ID: project_id,
226234
CONF_SECRET_KEY: secret_key,
227235
CONF_ENCRYPTION: encryption,
236+
CONF_ALLOW_RAW_COMMANDS: DEFAULT_ALLOW_RAW_COMMANDS,
228237
},
229238
)
230239
errors["base"] = "cannot_connect"
@@ -332,6 +341,7 @@ async def async_step_discovery_confirm(
332341
CONF_PROJECT_ID: project_id,
333342
CONF_SECRET_KEY: secret_key,
334343
CONF_ENCRYPTION: encryption,
344+
CONF_ALLOW_RAW_COMMANDS: DEFAULT_ALLOW_RAW_COMMANDS,
335345
},
336346
)
337347
errors["base"] = "cannot_connect"

custom_components/novastar_h/const.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313
CONF_PROJECT_ID = "project_id"
1414
CONF_SECRET_KEY = "secret_key"
1515
CONF_ENCRYPTION = "encryption"
16+
CONF_ALLOW_RAW_COMMANDS = "allow_raw_commands"
1617

1718
DEFAULT_DEVICE_ID = 0
1819
DEFAULT_SCREEN_ID = 0
1920
DEFAULT_ENCRYPTION = False
21+
DEFAULT_ALLOW_RAW_COMMANDS = False
2022

2123
PLATFORMS: list[Platform] = [
2224
Platform.SWITCH,

custom_components/novastar_h/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@
2121
}
2222
],
2323
"zeroconf": ["_novastar._tcp.local."],
24-
"version": "0.2.12"
24+
"version": "0.2.13"
2525
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
send_raw_command:
2+
name: Send Raw Command
3+
description: Send a raw API command to the Novastar processor (requires "Allow Raw Commands" option in configuration).
4+
fields:
5+
host:
6+
name: Host
7+
description: The IP address of the Novastar processor to send the command to.
8+
required: true
9+
example: "192.168.1.100"
10+
selector:
11+
text:
12+
endpoint:
13+
name: Endpoint
14+
description: The API endpoint path (e.g., "device/readDetail", "screen/writeBrightness").
15+
required: true
16+
example: "device/readDetail"
17+
selector:
18+
text:
19+
body:
20+
name: Body
21+
description: The JSON body to send with the request.
22+
required: true
23+
example: '{"deviceId": 0}'
24+
selector:
25+
object:

custom_components/novastar_h/strings.json

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@
1010
"project_id": "Project ID",
1111
"secret_key": "Secret Key",
1212
"encryption": "Enable Encryption",
13+
"allow_raw_commands": "Allow Raw Commands",
1314
"name": "Name"
1415
},
1516
"data_description": {
1617
"host": "IP address of your Novastar processor",
1718
"port": "API port (default: 8000)",
1819
"project_id": "From Settings > OpenAPI Management on your device",
1920
"secret_key": "From Settings > OpenAPI Management on your device",
20-
"encryption": "Enable encrypted communication with the device"
21+
"encryption": "Enable encrypted communication with the device",
22+
"allow_raw_commands": "Enable sending raw API commands via service call"
2123
}
2224
},
2325
"scan": {
@@ -36,14 +38,16 @@
3638
"project_id": "Project ID",
3739
"secret_key": "Secret Key",
3840
"encryption": "Enable Encryption",
41+
"allow_raw_commands": "Allow Raw Commands",
3942
"name": "Name"
4043
},
4144
"data_description": {
4245
"host": "IP address of your Novastar processor",
4346
"port": "API port (default: 8000)",
4447
"project_id": "From Settings > OpenAPI Management on your device",
4548
"secret_key": "From Settings > OpenAPI Management on your device",
46-
"encryption": "Enable encrypted communication with the device"
49+
"encryption": "Enable encrypted communication with the device",
50+
"allow_raw_commands": "Enable sending raw API commands via service call"
4751
}
4852
},
4953
"credentials": {
@@ -115,5 +119,25 @@
115119
"name": "Signal Power Status"
116120
}
117121
}
122+
},
123+
"services": {
124+
"send_raw_command": {
125+
"name": "Send Raw Command",
126+
"description": "Send a raw API command to the Novastar processor (requires 'Allow Raw Commands' option).",
127+
"fields": {
128+
"host": {
129+
"name": "Host",
130+
"description": "The IP address of the Novastar processor."
131+
},
132+
"endpoint": {
133+
"name": "Endpoint",
134+
"description": "The API endpoint path (e.g., 'device/readDetail')."
135+
},
136+
"body": {
137+
"name": "Body",
138+
"description": "The JSON body to send with the request."
139+
}
140+
}
141+
}
118142
}
119143
}

custom_components/novastar_h/translations/en.json

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@
1010
"project_id": "Project ID",
1111
"secret_key": "Secret Key",
1212
"encryption": "Enable Encryption",
13+
"allow_raw_commands": "Allow Raw Commands",
1314
"name": "Name"
1415
},
1516
"data_description": {
1617
"host": "IP address of your Novastar processor",
1718
"port": "API port (default: 8000)",
1819
"project_id": "From Settings > OpenAPI Management on your device",
1920
"secret_key": "From Settings > OpenAPI Management on your device",
20-
"encryption": "Enable encrypted communication with the device"
21+
"encryption": "Enable encrypted communication with the device",
22+
"allow_raw_commands": "Enable sending raw API commands via service call"
2123
}
2224
},
2325
"scan": {
@@ -36,14 +38,16 @@
3638
"project_id": "Project ID",
3739
"secret_key": "Secret Key",
3840
"encryption": "Enable Encryption",
41+
"allow_raw_commands": "Allow Raw Commands",
3942
"name": "Name"
4043
},
4144
"data_description": {
4245
"host": "IP address of your Novastar processor",
4346
"port": "API port (default: 8000)",
4447
"project_id": "From Settings > OpenAPI Management on your device",
4548
"secret_key": "From Settings > OpenAPI Management on your device",
46-
"encryption": "Enable encrypted communication with the device"
49+
"encryption": "Enable encrypted communication with the device",
50+
"allow_raw_commands": "Enable sending raw API commands via service call"
4751
}
4852
},
4953
"credentials": {
@@ -115,5 +119,25 @@
115119
"name": "Signal Power Status"
116120
}
117121
}
122+
},
123+
"services": {
124+
"send_raw_command": {
125+
"name": "Send Raw Command",
126+
"description": "Send a raw API command to the Novastar processor (requires 'Allow Raw Commands' option).",
127+
"fields": {
128+
"host": {
129+
"name": "Host",
130+
"description": "The IP address of the Novastar processor."
131+
},
132+
"endpoint": {
133+
"name": "Endpoint",
134+
"description": "The API endpoint path (e.g., 'device/readDetail')."
135+
},
136+
"body": {
137+
"name": "Body",
138+
"description": "The JSON body to send with the request."
139+
}
140+
}
141+
}
118142
}
119143
}

0 commit comments

Comments
 (0)