Skip to content

Commit 43f0cee

Browse files
authored
Serial for url (#76)
* Change to serial_for_url Replace Serial() by serial_for_url() * First go at adding support for tcp/ip * Remove data for schema * Catch serial exceptions * TCP also needs to know parity and baudrate * hacsfest * hacsfest
1 parent df96d85 commit 43f0cee

File tree

8 files changed

+192
-30
lines changed

8 files changed

+192
-30
lines changed

custom_components/ams/__init__.py

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@
2323
CONF_BAUDRATE,
2424
CONF_METER_MANUFACTURER,
2525
CONF_PARITY,
26+
CONF_PROTOCOL,
2627
CONF_SERIAL_PORT,
28+
CONF_TCP_HOST,
29+
CONF_TCP_PORT,
2730
DEFAULT_BAUDRATE,
2831
DEFAULT_METER_MANUFACTURER,
2932
DEFAULT_PARITY,
@@ -37,7 +40,9 @@
3740
KAIFA_METER_SEQ,
3841
KAIFA_SE_METER_SEQ,
3942
KAMSTRUP_METER_SEQ,
43+
NETWORK,
4044
SENSOR_ATTR,
45+
SERIAL,
4146
SIGNAL_NEW_AMS_SENSOR,
4247
SIGNAL_UPDATE_AMS
4348
)
@@ -115,22 +120,40 @@ class AmsHub:
115120

116121
def __init__(self, hass, entry):
117122
"""Initialize the AMS hub."""
123+
_LOGGER.debug("config entry = %s", entry)
118124
self._hass = hass
119-
port = entry.get(CONF_SERIAL_PORT)
120-
_LOGGER.debug("Connecting to HAN using port %s", port)
121-
parity = entry.get(CONF_PARITY)
122125
self.meter_manufacturer = entry.get(CONF_METER_MANUFACTURER)
123126
self.sensor_data = {}
124127
self._attrs = {}
125128
self._running = True
126-
self._ser = serial.Serial(
127-
port=port,
128-
baudrate=entry.get(CONF_BAUDRATE, DEFAULT_BAUDRATE),
129-
parity=parity,
130-
stopbits=serial.STOPBITS_ONE,
131-
bytesize=serial.EIGHTBITS,
132-
timeout=DEFAULT_TIMEOUT,
133-
)
129+
if entry.get(CONF_PROTOCOL) == SERIAL:
130+
port = entry.get(CONF_SERIAL_PORT)
131+
_LOGGER.debug("Connecting to HAN using serialport %s", port)
132+
try:
133+
self._ser = serial.serial_for_url(
134+
port,
135+
baudrate=entry.get(CONF_BAUDRATE),
136+
parity=entry.get(CONF_PARITY),
137+
stopbits=serial.STOPBITS_ONE,
138+
bytesize=serial.EIGHTBITS,
139+
timeout=DEFAULT_TIMEOUT,
140+
)
141+
except serial.serialutil.SerialException as ex:
142+
_LOGGER.warning("Serial error: %", ex)
143+
if entry.get(CONF_PROTOCOL) == NETWORK:
144+
port = f"socket://{entry.get(CONF_TCP_HOST)}:{entry.get(CONF_TCP_PORT)}"
145+
_LOGGER.debug("Connecting to HAN using TCP/IP %s", port)
146+
try:
147+
self._ser = serial.serial_for_url(
148+
port,
149+
baudrate=entry.get(CONF_BAUDRATE, DEFAULT_BAUDRATE),
150+
parity=entry.get(CONF_PARITY, DEFAULT_PARITY),
151+
stopbits=serial.STOPBITS_ONE,
152+
bytesize=serial.EIGHTBITS,
153+
timeout=DEFAULT_TIMEOUT,
154+
)
155+
except serial.serialutil.SerialException as ex:
156+
_LOGGER.warning("Serial error: %s", ex)
134157
self.connection = threading.Thread(target=self.connect, daemon=True)
135158
self.connection.start()
136159
_LOGGER.debug("Finish init of AMS")

custom_components/ams/config_flow.py

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,37 @@
1010
CONF_BAUDRATE,
1111
CONF_METER_MANUFACTURER,
1212
CONF_PARITY,
13+
CONF_PROTOCOL,
14+
CONF_TCP_HOST,
15+
CONF_TCP_PORT,
1316
CONF_SERIAL_PORT,
1417
DEFAULT_BAUDRATE,
1518
DEFAULT_METER_MANUFACTURER,
1619
DEFAULT_PARITY,
1720
DOMAIN,
18-
MANUFACTURER_OPTIONS
21+
NETWORK,
22+
MANUFACTURER_OPTIONS,
23+
SERIAL,
24+
)
25+
DATA_SCHEMA_SELECT_PROTOCOL = vol.Schema(
26+
{vol.Required("type"): vol.In([SERIAL, NETWORK])}
27+
)
28+
DATA_SCHEMA_NETWORK_DATA = vol.Schema(
29+
{
30+
vol.Required(CONF_TCP_HOST): str,
31+
vol.Required(CONF_TCP_PORT): vol.All(vol.Coerce(int), vol.Range(0, 65535)),
32+
vol.Required(
33+
CONF_METER_MANUFACTURER,
34+
default=DEFAULT_METER_MANUFACTURER
35+
): vol.In(MANUFACTURER_OPTIONS),
36+
vol.Optional(
37+
CONF_PARITY, default=DEFAULT_PARITY
38+
): vol.All(str),
39+
vol.Optional(
40+
CONF_BAUDRATE, default=DEFAULT_BAUDRATE
41+
): vol.All(int),
42+
}
1943
)
20-
2144
_LOGGER = logging.getLogger(__name__)
2245

2346

@@ -30,25 +53,44 @@ class AmsFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
3053
def __init__(self):
3154
"""Initialize."""
3255
self._errors = {}
56+
self.connection_type = None
3357

3458
async def async_step_user(self, user_input=None):
35-
"""Handle a flow initialized by the user."""
59+
"""Handle selection of protocol."""
60+
if user_input is not None:
61+
self.connection_type = user_input["type"]
62+
if self.connection_type == NETWORK:
63+
return await self.async_step_network_connection()
64+
if self.connection_type == SERIAL:
65+
return await self.async_step_serial_connection()
66+
67+
return self.async_show_form(
68+
step_id="user", data_schema=DATA_SCHEMA_SELECT_PROTOCOL, errors=self._errors
69+
)
70+
71+
async def async_step_serial_connection(self, user_input=None):
72+
"""Handle the serialport connection step."""
3673
portdata = await self.hass.async_add_executor_job(devices.comports)
3774
ports = [(comport.device + ": " + comport.description) for
3875
comport in portdata]
3976

4077
if user_input is not None:
78+
user_input[CONF_PROTOCOL] = self.connection_type
4179
user_selection = user_input[CONF_SERIAL_PORT]
4280
port = portdata[ports.index(user_selection)]
4381
serial_by_id = await self.hass.async_add_executor_job(
4482
get_serial_by_id, port.device
4583
)
4684
user_input[CONF_SERIAL_PORT] = serial_by_id
47-
return self.async_create_entry(title="AMS Reader",
48-
data=user_input)
85+
entry_result = self.async_create_entry(
86+
title="AMS Reader", data=user_input,
87+
)
88+
if entry_result:
89+
return entry_result
90+
4991
_LOGGER.debug(ports)
5092
return self.async_show_form(
51-
step_id="user",
93+
step_id="serial_connection",
5294
data_schema=vol.Schema(
5395
{
5496
vol.Required(
@@ -66,15 +108,25 @@ async def async_step_user(self, user_input=None):
66108
): vol.All(int),
67109
}
68110
),
69-
description_placeholders={
70-
CONF_SERIAL_PORT: ports,
71-
CONF_METER_MANUFACTURER: MANUFACTURER_OPTIONS,
72-
CONF_PARITY: DEFAULT_PARITY,
73-
CONF_BAUDRATE: DEFAULT_BAUDRATE,
74-
},
75111
errors=self._errors,
76112
)
77113

114+
async def async_step_network_connection(self, user_input=None):
115+
"""Handle the network connection step."""
116+
if user_input:
117+
user_input[CONF_PROTOCOL] = self.connection_type
118+
entry_result = self.async_create_entry(
119+
title="AMS Reader", data=user_input,
120+
)
121+
if entry_result:
122+
return entry_result
123+
124+
return self.async_show_form(
125+
step_id="network_connection",
126+
data_schema=DATA_SCHEMA_NETWORK_DATA,
127+
errors={},
128+
)
129+
78130
async def async_step_import(self, import_config):
79131
"""Import a config flow from configuration."""
80132
if self._async_current_entries():

custom_components/ams/const.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,17 @@
5454
CONF_METER_MANUFACTURER = HAN_METER_MANUFACTURER
5555
CONF_PARITY = "parity"
5656
CONF_SERIAL_PORT = "serial_port"
57+
CONF_TCP_PORT = "tcp_port"
58+
CONF_TCP_HOST = "tcp_host"
59+
CONF_PROTOCOL = "protocol"
60+
CONF_PROTOCOL_CONFIG = "protocol_config"
5761

5862
ATTR_DEVICE_CLASS = "device_class"
5963
ATTR_LAST_RESET = "last_reset"
6064
ATTR_STATE_CLASS = "state_class"
6165
DEVICE_CLASS_ENERGY = "energy"
66+
SERIAL = "Serial port"
67+
NETWORK = "TCP/IP"
6268
STATE_CLASS_TOTAL_INCREASING = "total_increasing"
6369

6470
DOMAIN = "ams"

custom_components/ams/translations/en.json

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,28 @@
66
"title": "AMS Reader",
77
"step": {
88
"user": {
9-
"description": "Setup AMS sensors",
9+
"description": "Choose protocol for communications",
10+
"data": {
11+
"type": "Protocol type"
12+
}
13+
},
14+
"network_connection": {
15+
"description": "Setup options for TCP/IP connection",
16+
"data": {
17+
"tcp_host": "IP address",
18+
"tcp_port": "Port",
19+
"parity": "Parity",
20+
"baudrate": "Baud rate",
21+
"meter_manufacturer": "Meter manufacturer"
22+
}
23+
},
24+
"serial_connection": {
25+
"description": "Setup options for serial communication",
1026
"data": {
1127
"serial_port": "Serial Port",
1228
"parity": "Parity",
1329
"meter_manufacturer": "Meter manufacturer",
14-
"baudrate": "Baudrate"
30+
"baudrate": "Baud rate"
1531
}
1632
}
1733
},

custom_components/ams/translations/nb.json

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,25 @@
33
"abort": {
44
"single_instance_allowed": "Kun en oppføring av hass-AMS er tillatt"
55
},
6-
"title": "AMS Leser",
76
"step": {
87
"user": {
9-
"description": "Installer sensorer for AMS leser",
8+
"description": "Velg kommunikasjonsprotokoll for inndata",
9+
"data":{
10+
"type": "Protokolltype"
11+
}
12+
},
13+
"network_connection": {
14+
"description": "Velg data for TCP/IP tilkobling",
15+
"data": {
16+
"tcp_host": "IP adresse",
17+
"tcp_port": "Port",
18+
"parity": "Paritet",
19+
"baudrate": "Baudrate",
20+
"meter_manufacturer": "Måler produsent"
21+
}
22+
},
23+
"serial_connection": {
24+
"description": "Velg portdata for serietilkobling",
1025
"data": {
1126
"serial_port": "Serieport",
1227
"parity": "Paritet",

custom_components/ams/translations/nn.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,25 @@
66
"title": "AMS Lesar",
77
"step": {
88
"user": {
9-
"description": "Installerar sensorar for AMS Lesar",
9+
"description": "Velg kommunikasjonsprotokoll for inndata",
10+
"data": {
11+
"type": "Protokolltype"
12+
}
13+
},
14+
"network_connection": {
15+
"title": "AMS Lesar",
16+
"description": "Vel data for TCP/IP tilkobling",
17+
"data": {
18+
"tcp_host": "IP adresse",
19+
"tcp_port": "Port",
20+
"parity": "Paritet",
21+
"baudrate": "Baudrate",
22+
"meter_manufacturer": "Målar produsent"
23+
}
24+
},
25+
"serial_connection": {
26+
"title": "AMS Noreg",
27+
"description": "Vel portdata for serietilkobling",
1028
"data": {
1129
"serial_port": "Serieport",
1230
"parity": "Paritet",

custom_components/ams/translations/no.json

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,23 @@
66
"title": "AMS Leser",
77
"step": {
88
"user": {
9-
"description": "Installer sensorer for AMS i Norge",
9+
"description": "Velg kommunikasjonsprotokoll for inndata",
10+
"data":{
11+
"type": "Protokolltype"
12+
}
13+
},
14+
"network_connection": {
15+
"description": "Velg data for TCP/IP tilkobling",
16+
"data": {
17+
"tcp_host": "IP adresse",
18+
"tcp_port": "Port",
19+
"parity": "Paritet",
20+
"baudrate": "Baudrate",
21+
"meter_manufacturer": "Måler produsent"
22+
}
23+
},
24+
"serial_connection": {
25+
"description": "Velg portdata for serietilkobling",
1026
"data": {
1127
"serial_port": "Serieport",
1228
"parity": "Paritet",

custom_components/ams/translations/se.json

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,23 @@
66
"title": "AMS Reader",
77
"step": {
88
"user": {
9-
"description": "Installera sensor för AMS läsare",
9+
"description": "Välj kommunikastionsprotokoll för inndata",
10+
"data":{
11+
"type": "Protokolltyp"
12+
}
13+
},
14+
"network_connection": {
15+
"description": "Välj data för TCP/IP tilkobling",
16+
"data": {
17+
"tcp_host": "IP adress",
18+
"tcp_port": "Port",
19+
"parity": "Paritet",
20+
"baudrate": "Baudrate",
21+
"meter_manufacturer": "Mätartilverkare"
22+
}
23+
},
24+
"serial_connection": {
25+
"description": "Välj data for seriell anslutning",
1026
"data": {
1127
"serial_port": "Serieport",
1228
"parity": "Paritet",

0 commit comments

Comments
 (0)