Skip to content

Commit 8328e24

Browse files
committed
Version 2.0 with config flow and manual configuration
1 parent 827b04c commit 8328e24

30 files changed

+7360
-775
lines changed

.devcontainer/configuration.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ logger:
2020
# - url: !secret ihc_url
2121
# username: !secret ihc_username
2222
# password: !secret ihc_password
23-
# auto_setup: false
23+
# auto_setup: true
2424

25-
ihcviewer:
25+
# ihcviewer:
2626

2727
debugpy:
2828
wait: true

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "homeassistant-frontend"]
2+
path = homeassistant-frontend
3+
url = https://github.com/home-assistant/frontend

.vscode/launch.json

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
// Example of attaching to local debug server running on WSL
6+
"name": "Python: Attach Local",
7+
"type": "python",
8+
"request": "attach",
9+
"port": 5678,
10+
"host": "localhost",
11+
"pathMappings": [
12+
{
13+
"localRoot": "G:\\Dev\\python\\homeassistant\\haihcviewer",
14+
"remoteRoot": "/mnt/g/Dev/python/homeassistant/haihcviewer"
15+
}
16+
]
17+
}
18+
]
19+
}

.vscode/settings.json

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
{
2+
"python.linting.pylintEnabled": true,
3+
"python.linting.enabled": true,
4+
"python.pythonPath": "/usr/bin/python",
25
"python.formatting.provider": "black",
36
"files.associations": {
4-
"*.yaml": "home-assistant"
5-
}
6-
}
7+
"*.yaml": "home-assistant"
8+
},
9+
}

custom_components/ihcviewer/__init__.py

+63-118
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,79 @@
44
see http://www.dingus.dk for more information
55
"""
66
import logging
7-
import asyncio
8-
import base64
97
import os.path
10-
from aiohttp import web
118

12-
import homeassistant.core as ha
13-
from homeassistant.components.ihc import IHC_CONTROLLER
14-
from homeassistant.components.http import HomeAssistantView
9+
from homeassistant.core import HomeAssistant
10+
from homeassistant.config_entries import ConfigEntry
11+
12+
from .const import DOMAIN, NAME_SHORT, URL_PANEL, VERSION
13+
14+
from .api.getresource import ApiGetResource
15+
from .api.log import ApiLog
16+
from .api.manual_binarysensor import ApiManualBinarySensor
17+
from .api.manual_light import ApiManualLight
18+
from .api.manual_remove import ApiManualRemove
19+
from .api.manual_sensor import ApiManualSensor
20+
from .api.manual_switch import ApiManualSwitch
21+
from .api.mapping import ApiMapping
22+
from .api.project import ApiProject
23+
from .api.setboolresource import ApiSetBoolResource
24+
from .api.systeminfo import ApiSystemInfo
1525

16-
from custom_components.ihcviewer.const import DOMAIN, NAME_SHORT, VERSION
1726

18-
REQUIREMENTS = ["ihcsdk==2.7.0"]
1927
DEPENDENCIES = ["ihc"]
2028

2129
_LOGGER = logging.getLogger(__name__)
2230

23-
# We hold a global cached list of the mapping
24-
ihcmapping = None
31+
MANUAL_SETUP_YAML = "ihc_manual_setup.yaml"
2532

2633

27-
async def async_setup(hass, config):
34+
async def async_setup(hass: HomeAssistant, config):
2835
"""Setup the IHC viewer component."""
29-
conf = config[DOMAIN]
30-
controller_id = 0
31-
if "ihc" in hass.data:
32-
# We use the first controller
33-
controllerid1 = next(iter(hass.data["ihc"]))
34-
ihc_controller = hass.data["ihc"][controllerid1][IHC_CONTROLLER]
35-
else:
36-
ihc_controller = hass.data[f"ihc{controller_id}"][IHC_CONTROLLER]
37-
hass.http.register_view(IhcViewerApiView(ihc_controller, hass))
36+
37+
if DOMAIN in config and config is not None:
38+
_LOGGER.error("Setup using configuration is not supported anymore")
39+
return False
3840

3941
register_frontend(hass)
40-
add_side_panel(hass, conf)
42+
hass.http.register_view(ApiGetResource(hass))
43+
hass.http.register_view(ApiLog(hass))
44+
hass.http.register_view(ApiManualBinarySensor(hass))
45+
hass.http.register_view(ApiManualLight(hass))
46+
hass.http.register_view(ApiManualRemove(hass))
47+
hass.http.register_view(ApiManualSensor(hass))
48+
hass.http.register_view(ApiManualSwitch(hass))
49+
hass.http.register_view(ApiMapping(hass))
50+
hass.http.register_view(ApiProject(hass))
51+
hass.http.register_view(ApiSetBoolResource(hass))
52+
hass.http.register_view(ApiSystemInfo(hass))
53+
return True
54+
55+
56+
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
57+
"""Set up the IHC Viewer from a config entry."""
58+
59+
if "ihc" not in hass.data:
60+
if "ihc0" in hass.data:
61+
_LOGGER.error(
62+
"IHCViewer 2.x does not support the old IHC integration. You must update your IHC integration"
63+
)
64+
else:
65+
_LOGGER.error("IHC integration is not loaded")
66+
return False
67+
hass.data.setdefault(DOMAIN, {})
68+
hass.data[DOMAIN] = list(dict(hass.data["ihc"]).keys())
69+
add_side_panel(hass)
4170
return True
4271

4372

44-
def add_side_panel(hass, conf):
73+
async def async_unload_entry(hass, entry):
74+
"""Unload a config entry."""
75+
hass.components.frontend.async_remove_panel(hass, URL_PANEL)
76+
return await hass.data[DOMAIN].async_unload_entry(entry)
77+
78+
79+
def add_side_panel(hass):
4580
"""Add the IHCViewer sidepanel"""
4681

4782
custom_panel_config = {
@@ -50,19 +85,16 @@ def add_side_panel(hass, conf):
5085
"trust_external": False,
5186
"module_url": f"/ihcviewer/frontend-{VERSION}/panel.js",
5287
}
53-
if conf is not None:
54-
# Make copy because we're mutating it
55-
conf = dict(conf)
56-
else:
57-
conf = {}
58-
conf["_panel_custom"] = custom_panel_config
59-
conf["version"] = VERSION
88+
panelconf = {}
89+
panelconf["_panel_custom"] = custom_panel_config
90+
panelconf["version"] = VERSION
91+
panelconf[DOMAIN] = hass.data[DOMAIN]
6092
hass.components.frontend.async_register_built_in_panel(
6193
component_name="custom",
62-
frontend_url_path="ihc_viewer",
94+
frontend_url_path=URL_PANEL,
6395
sidebar_title=NAME_SHORT,
6496
sidebar_icon="mdi:file-tree",
65-
config=conf,
97+
config=panelconf,
6698
require_admin=True,
6799
)
68100
return
@@ -79,90 +111,3 @@ def register_frontend(hass):
79111
f"/ihcviewer/frontend-{VERSION}/{filename}",
80112
os.path.join(path, filename),
81113
)
82-
83-
84-
class IhcViewerApiView(HomeAssistantView):
85-
"""IHCViewer api requests."""
86-
87-
requires_auth = False
88-
name = "api:ihcviewer"
89-
url = r"/api/ihcviewer/{requested_file:.+}"
90-
91-
def __init__(self, ihc_controller, hass):
92-
"""Initilalize the IHCGetValue."""
93-
self.hass = hass
94-
self.ihc_controller = ihc_controller
95-
96-
@ha.callback
97-
async def get(self, request, requested_file): # pylint: disable=unused-argument
98-
"""Handle api Web requests."""
99-
100-
if requested_file == "log":
101-
log = await self.hass.async_add_executor_job(self.get_user_log)
102-
return web.Response(
103-
body=log, content_type="text/plain", charset="utf-8", status=200
104-
)
105-
106-
if requested_file == "project":
107-
project = await self.hass.async_add_executor_job(
108-
self.ihc_controller.get_project
109-
)
110-
return web.Response(
111-
body=project, content_type="text/xml", charset="utf-8", status=200
112-
)
113-
114-
if requested_file == "getvalue":
115-
data = request.query
116-
id = int(data.get("id"))
117-
return await self.get_value(id)
118-
119-
if requested_file == "mapping":
120-
return await self.get_mapping()
121-
122-
return web.Response(status=404)
123-
124-
async def get_value(self, id):
125-
value = await self.hass.async_add_executor_job(
126-
self.ihc_controller.client.get_runtime_value, id
127-
)
128-
entity_id = ""
129-
global ihcmapping
130-
if ihcmapping is not None and id in ihcmapping:
131-
entity_id = ihcmapping[id]
132-
json = {"value": value, "type": type(value).__name__, "entity": entity_id}
133-
return self.json(json)
134-
135-
async def get_mapping(self):
136-
global ihcmapping
137-
ihcmapping = {}
138-
allstates = await self.hass.async_add_executor_job(self.hass.states.all)
139-
for state in allstates:
140-
if "ihc_id" in state.attributes:
141-
ihcmapping[state.attributes["ihc_id"]] = state.entity_id
142-
return self.json(ihcmapping)
143-
144-
ihcns = {
145-
"SOAP-ENV": "http://schemas.xmlsoap.org/soap/envelope/",
146-
"ns1": "utcs",
147-
"ns2": "utcs.values",
148-
"ns3": "utcs.values",
149-
}
150-
151-
def get_user_log(self, language="en"):
152-
payload = """<getUserLog1 xmlns="utcs" />
153-
<getUserLog2 xmlns="utcs">0</getUserLog2>
154-
<getUserLog3 xmlns="utcs">{language}</getUserLog3>
155-
""".format(
156-
language=language
157-
)
158-
xdoc = self.ihc_controller.client.connection.soap_action(
159-
"/ws/ConfigurationService", "getUserLog", payload
160-
)
161-
if xdoc is not None:
162-
base64data = xdoc.find(
163-
"./SOAP-ENV:Body/ns1:getUserLog4/ns1:data", IhcViewerApiView.ihcns
164-
).text
165-
if not base64data:
166-
return ""
167-
return base64.b64decode(base64data).decode("UTF-8")
168-
return ""

custom_components/ihcviewer/const.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
NAME_SHORT = "IHCViewer"
22
DOMAIN = "ihcviewer"
3-
VERSION = "1.2.4"
3+
VERSION = "1.2.6"
44
PROJECT_URL = "https://github.com/dingusdk/haihcviewer/"
55
ISSUE_URL = f"{PROJECT_URL}issues"
6+
7+
CONF_CONTROLLER_ID = "controller_id"
8+
9+
IHC_PLATFORMS = ("binary_sensor", "light", "sensor", "switch")
10+
11+
URL_PANEL = "ihc_viewer"

0 commit comments

Comments
 (0)