diff --git a/custom_components/shelly/__init__.py b/custom_components/shelly/__init__.py index 7ff5fad..b491179 100755 --- a/custom_components/shelly/__init__.py +++ b/custom_components/shelly/__init__.py @@ -59,7 +59,7 @@ _LOGGER = logging.getLogger(__name__) -__version__ = "0.3.2" +__version__ = "0.3.3" VERSION = __version__ async def async_setup(hass, config): @@ -132,7 +132,7 @@ def __init__(self, hass, config_entry, conf): self.conf = conf self.version_added = False self.discover = self.conf.get(CONF_DISCOVERY) - self.device_sensors = [] #Keep track dynamic device sensors is added + #self.device_sensors = [] #Removed, cause collisions self.block_sensors = [] #Keep track dynamic block sensors is added self.update_config_attributes() sensors = self.conf.get(CONF_SENSORS, {}) @@ -219,7 +219,7 @@ def set_config(self, id, value): def update_config_list(self, type, id, value): options = self.config_entry.options.copy() - list = options[type] = options[type].copy() + list = options[type] = options.get(type, []).copy() if value: if not id in list: list.append(id) diff --git a/custom_components/shelly/binary_sensor.py b/custom_components/shelly/binary_sensor.py index 4a25220..cabdf60 100644 --- a/custom_components/shelly/binary_sensor.py +++ b/custom_components/shelly/binary_sensor.py @@ -130,8 +130,8 @@ def update(self): self._last_event = event @property - def device_state_attributes(self): - attrs = super().device_state_attributes + def extra_state_attributes(self): + attrs = super().extra_state_attributes if self._last_event: attrs[ATTRIBUTE_CLICK_TYPE] = self._last_event attrs[ATTRIBUTE_CLICK_CNT] = self._event_cnt diff --git a/custom_components/shelly/block.py b/custom_components/shelly/block.py index 170f4bb..6d220b0 100644 --- a/custom_components/shelly/block.py +++ b/custom_components/shelly/block.py @@ -73,7 +73,7 @@ def _updated(self, _block): self._update_ha_state() @property - def device_state_attributes(self): + def extra_state_attributes(self): """Show state attributes in HASS""" attrs = {'shelly_type': self._block.type_name(), 'shelly_id': self._block.id, diff --git a/custom_components/shelly/const.py b/custom_components/shelly/const.py index 6390b3b..da32e67 100644 --- a/custom_components/shelly/const.py +++ b/custom_components/shelly/const.py @@ -102,32 +102,46 @@ ] ALL_CONFIG = { - CONF_ENTITY_ID : { "type" : "str" }, - CONF_LIGHT_SWITCH : { "type" : "bool" }, - CONF_ADDITIONAL_INFO : { "type" : "bool" }, - CONF_IGMPFIX : { "type" : "bool" }, - CONF_MDNS : { "type" : "bool" }, - CONF_OBJECT_ID_PREFIX : { "type" : "str" }, - CONF_SHOW_ID_IN_NAME : { "type" : "bool" }, - CONF_VERSION : { "type" : "bool" }, - CONF_UPGRADE_SWITCH : { "type" : "bool" }, - CONF_UPGRADE_BETA_SWITCH : { "type" : "bool" }, - CONF_UNAVALABLE_AFTER_SEC : { "type" : "int" }, - CONF_CLOUD_AUTH_KEY : { "type" : "txt" }, - CONF_CLOUD_SERVER : { "type" : "str" }, - CONF_TMPL_NAME : { "type" : "str" }, + #CONF_ENTITY_ID : { "type" : "str" }, + #CONF_LIGHT_SWITCH : { "type" : "bool" }, + #CONF_OBJECT_ID_PREFIX : { "type" : "str" }, #CONF_DISCOVER_BY_IP : { "type" : "list" }, - CONF_DISCOVERY : { "type" : "bool" }, - CONF_HOST_IP : { "type" : "str" }, - CONF_MQTT_PORT : { "type" : "int", "group" : "mqtt" }, - CONF_MQTT_SERVER_HOST : { "type" : "str", "group" : "mqtt-server" }, - CONF_MQTT_SERVER_PORT : { "type" : "int", "group" : "mqtt-server" }, - CONF_MQTT_SERVER_USERNAME : { "type" : "str", "group" : "mqtt-server" }, - CONF_MQTT_SERVER_PASSWORD : {"type" : "str", "group" : "mqtt-server" }, + + #General + CONF_VERSION : { "type" : "bool", "group": "general" }, + CONF_UPGRADE_SWITCH : { "type" : "bool", "group": "general" }, + CONF_UPGRADE_BETA_SWITCH : { "type" : "bool", "group": "general" }, + CONF_ADDITIONAL_INFO : { "type" : "bool", "group": "general" }, + CONF_UNAVALABLE_AFTER_SEC : { "type" : "int", "group": "general" }, + CONF_SCAN_INTERVAL : { "type" : "int", "group" : "general" }, + + #Discovery + CONF_IGMPFIX : { "type" : "bool", "group": "discovery" }, + CONF_MDNS : { "type" : "bool", "group": "discovery" }, + CONF_DISCOVERY : { "type" : "bool", "group": "discovery" }, + CONF_HOST_IP : { "type" : "str", "group": "discovery" }, + + #Name + CONF_SHOW_ID_IN_NAME : { "type" : "bool", "group": "name" }, + CONF_TMPL_NAME : { "type" : "str", "group": "name" }, + + #Cloud + CONF_CLOUD_AUTH_KEY : { "type" : "txt", "group": "cloud" }, + CONF_CLOUD_SERVER : { "type" : "str", "group": "cloud" }, + + #MQTT integrated + CONF_MQTT_PORT : { "type" : "int", "group" : "mqtt-integrated" }, + + #MQTT broker + CONF_MQTT_SERVER_HOST : { "type" : "str", "group" : "mqtt-broker" }, + CONF_MQTT_SERVER_PORT : { "type" : "int", "group" : "mqtt-broker" }, + CONF_MQTT_SERVER_USERNAME : { "type" : "str", "group" : "mqtt-broker" }, + CONF_MQTT_SERVER_PASSWORD : {"type" : "str", "group" : "mqtt-broker" }, + + #Debug CONF_LOCAL_PY_SHELLY : { "type" : "bool", "group" : "debug" }, CONF_ONLY_DEVICE_ID : {"type" : "str", "group" : "debug" }, - CONF_DEBUG_ENABLE_INFO : {"type" : "bool", "group" : "debug"}, - CONF_SCAN_INTERVAL : { "type" : "int", "group" : "general" } + CONF_DEBUG_ENABLE_INFO : {"type" : "bool", "group" : "debug"} } DEFAULT_IGMPFIX = False diff --git a/custom_components/shelly/device.py b/custom_components/shelly/device.py index e5e0118..888efd1 100644 --- a/custom_components/shelly/device.py +++ b/custom_components/shelly/device.py @@ -58,11 +58,11 @@ def _updated(self, _block): self._update_ha_state() if self._dev.info_values is not None: - device_sensors = self.instance.device_sensors + block_sensors = self.instance.block_sensors for key, _value in self._dev.info_values.items(): ukey = self._dev.id + '-' + key - if not ukey in device_sensors: - device_sensors.append(ukey) + if not ukey in block_sensors: + block_sensors.append(ukey) for sensor in self._sensor_conf: if ALL_SENSORS[sensor].get('attr') == key: attr = {'sensor_type':key, @@ -113,7 +113,7 @@ def _debug_add_state_info(self, attrs): attrs['state_MQTT_status'] = self._dev.state_mqtt_status @property - def device_state_attributes(self): + def extra_state_attributes(self): """Show state attributes in HASS""" attrs = {'shelly_type': self._dev.type_name(), 'shelly_id': self._dev.id, diff --git a/custom_components/shelly/frontend/rollup.config.js b/custom_components/shelly/frontend/rollup.config.js index 3b8e79d..b240299 100644 --- a/custom_components/shelly/frontend/rollup.config.js +++ b/custom_components/shelly/frontend/rollup.config.js @@ -8,6 +8,8 @@ import typescript from "rollup-plugin-typescript2"; import { visualizer } from 'rollup-plugin-visualizer'; import styles from "rollup-plugin-styles"; +const production = !process.env.ROLLUP_WATCH; + export default { input: "src/index.ts", output: { @@ -51,6 +53,6 @@ export default { host: "localhost", port: 3000, }),*/ - //livereload({ watch: "dist" }), + !production && livereload({ watch: "dist" }), ] }; diff --git a/custom_components/shelly/frontend/src/components/ConfigEntry.tsx b/custom_components/shelly/frontend/src/components/ConfigEntry.tsx index 6d717be..1857fa6 100644 --- a/custom_components/shelly/frontend/src/components/ConfigEntry.tsx +++ b/custom_components/shelly/frontend/src/components/ConfigEntry.tsx @@ -28,21 +28,21 @@ export default class ShellyConfigEntry extends Component { if (config.type=="bool") { const val = config.value.toString().toLowerCase()=="true"; - return + return } if (config.type=="str") - return if (config.type=="txt") - return if (config.type=="int") - return diff --git a/custom_components/shelly/frontend/src/components/setting/Checkbox.tsx b/custom_components/shelly/frontend/src/components/setting/Checkbox.tsx index 9a5d9da..0a6b03e 100644 --- a/custom_components/shelly/frontend/src/components/setting/Checkbox.tsx +++ b/custom_components/shelly/frontend/src/components/setting/Checkbox.tsx @@ -30,11 +30,11 @@ export default class ShellySettingCheckbox extends Component { const { setting, param } = this.props; return
{setting.has[param] ? - <> - {param}:  - + +  {param} : ""} diff --git a/custom_components/shelly/frontend/src/components/setting/TextField.tsx b/custom_components/shelly/frontend/src/components/setting/TextField.tsx index 618cffa..f5f9503 100644 --- a/custom_components/shelly/frontend/src/components/setting/TextField.tsx +++ b/custom_components/shelly/frontend/src/components/setting/TextField.tsx @@ -32,7 +32,7 @@ export default class SettingTextField extends Component { return
{setting.has[param] ? <>
{param}:
- diff --git a/custom_components/shelly/frontend/src/data.ts b/custom_components/shelly/frontend/src/data.ts index eb42a18..4f22e0d 100644 --- a/custom_components/shelly/frontend/src/data.ts +++ b/custom_components/shelly/frontend/src/data.ts @@ -10,7 +10,9 @@ export interface Instance { export interface Config { id : string, - name : string, + title : string, + desc: string, + group: string, type : string, value : string, def : string @@ -18,8 +20,7 @@ export interface Config { export interface Setting { id : string, - name : string, - image : string, + title : string, value : { sensor : boolean, attrib: boolean, @@ -47,7 +48,8 @@ export interface App { export const getConfiguration = async (hass: HomeAssistant) => { const response = await hass.connection.sendMessagePromise({ - type: "s4h/get_config" + type: "s4h/get_config", + language: hass.language }); response.instances.map( i => { i.hass=hass }) return response; diff --git a/custom_components/shelly/frontend/src/panels/ConfigPanel.tsx b/custom_components/shelly/frontend/src/panels/ConfigPanel.tsx index 267b379..6d51e07 100644 --- a/custom_components/shelly/frontend/src/panels/ConfigPanel.tsx +++ b/custom_components/shelly/frontend/src/panels/ConfigPanel.tsx @@ -15,19 +15,27 @@ export default class ShellyConfigPanel extends Component { return
Loading!!!!!!
; const { app, instance } = this.props; const { configs } = instance; + const groups = [...new Set(configs.map( config => config.group ))]; + return (<>

Config

- {configs.map( config => ( - -
{config.name}
-
- -
-
+ {groups.map( group => ( +
+
{group}
+ {configs.filter( config => config.group==group).map( config => ( + + {config.title} +
{config.desc}
+
+ +
+
+ ))} +
))}
diff --git a/custom_components/shelly/frontend/src/panels/Panel.scss b/custom_components/shelly/frontend/src/panels/Panel.scss index 0314aad..7371fbe 100644 --- a/custom_components/shelly/frontend/src/panels/Panel.scss +++ b/custom_components/shelly/frontend/src/panels/Panel.scss @@ -45,35 +45,72 @@ $card-bg : var(--secondary-background-color); padding: 0.8rem; margin: 0.1rem; position: relative; + vertical-align: top; + border-radius: 0.4rem; } .configpanel { + .group { + width: 19rem; + padding: 10px; + margin: 5px; + border: 1px solid var(--secondary-text-color); + border-radius: 10px; + .title { + font-size: 18px; + font-weight: bold; + margin-bottom: 10px; + } + } + .card-title { + font-size: var(--ha-card-header-font-size); + font-weight: var(--ha-card-header-font-weight); + } + .desc { + color: var(--secondary-text-color); + margin-bottom: 10px; + } .value { .bool { position: absolute; top: 1rem; right: 1rem; } + textarea { + width: 95%; + } } } .settingpanel { - .name { - padding-bottom: 0.7rem; - font-weight: 500; + .card { + height: 11rem; + margin: 0.2rem; + } + .card-title { + font-size: var(--ha-card-header-font-size); + font-weight: var(--ha-card-header-font-weight); } .label { - width: 5rem; + padding-left: 0.5rem; + width: 5.5rem; display: inline-block; + color: var(--secondary-text-color); } input[type="text"], input[type="number"] { width: 8rem; - margin-top: 0.3rem; + margin-top: 0.5rem; + } + .decimals { + margin-top: 0.2rem; } .attrib { display: inline-block; + vertical-align: top; } - .sensor { - float: right; + .sensor { + margin-right: 1rem; + display: inline-block; + vertical-align: top; } } diff --git a/custom_components/shelly/frontend/src/panels/SettingPanel.tsx b/custom_components/shelly/frontend/src/panels/SettingPanel.tsx index 80626fa..b7bf4f4 100644 --- a/custom_components/shelly/frontend/src/panels/SettingPanel.tsx +++ b/custom_components/shelly/frontend/src/panels/SettingPanel.tsx @@ -15,13 +15,10 @@ export default class ShellySettingPanel extends Component { const { instance } = this.props return (

Settings

- {instance.settings.map( setting => ( -
{setting.name}
- Show:  + {setting.title} +
show:
@@ -29,7 +26,6 @@ export default class ShellySettingPanel extends Component {
))} -
); } diff --git a/custom_components/shelly/manifest.json b/custom_components/shelly/manifest.json index 2f1311b..cbe9b21 100644 --- a/custom_components/shelly/manifest.json +++ b/custom_components/shelly/manifest.json @@ -1,11 +1,11 @@ { "domain": "shelly", "name": "Shelly smart home", - "version": "0.3.2", + "version": "0.3.3", "config_flow": true, "documentation": "https://github.com/StyraHem/ShellyForHASS/blob/master/README.md", "dependencies": ["zeroconf"], "codeowners": ["@hakana","@StyraHem"], - "requirements": ["pyShelly==0.3.4", "paho-mqtt==1.5.1", "websocket-client"], + "requirements": ["pyShelly==0.3.5", "paho-mqtt==1.5.1", "websocket-client"], "iot_class": "local_push" } diff --git a/custom_components/shelly/sensor.py b/custom_components/shelly/sensor.py index ccdf08d..7b73a61 100755 --- a/custom_components/shelly/sensor.py +++ b/custom_components/shelly/sensor.py @@ -209,7 +209,7 @@ def state(self): return self._version #+ "/" + self._py_shelly_version @property - def device_state_attributes(self): + def extra_state_attributes(self): """Return attributes for the sensor.""" attribs = {'shelly': self._version, 'pyShelly': self._py_shelly_version, diff --git a/custom_components/shelly/switch.py b/custom_components/shelly/switch.py index 5054939..7d0bddf 100755 --- a/custom_components/shelly/switch.py +++ b/custom_components/shelly/switch.py @@ -117,8 +117,8 @@ def name(self): return ShellyBlock.name.fget(self) + " - Download new firmware" @property - def device_state_attributes(self): - attrs = super().device_state_attributes + def extra_state_attributes(self): + attrs = super().extra_state_attributes latest_key = ATTRIBUTE_LATEST_BETA_FW if self._beta else ATTRIBUTE_LATEST_FW attrs[latest_key] = \ self._block.info_values[latest_key] diff --git a/custom_components/shelly/translations/en.json b/custom_components/shelly/translations/en.json index bffcf44..3bd635c 100644 --- a/custom_components/shelly/translations/en.json +++ b/custom_components/shelly/translations/en.json @@ -1,4 +1,138 @@ { + "frontend": { + "settings" : { + "ip_address": "IP address", + "shelly_type": "Shelly type", + "shelly_id": "Shelly ID", + "ssid": "SSID", + "rssi": "RSSI", + "rssi_level": "RSSI Level", + "uptime": "Uptime", + "has_firmware_update": "Has firmware update", + "latest_fw_version": "Latest firmware version", + "firmware_version": "Current firmware version", + "mqtt_connected": "MQTT connected", + "cloud_status": "Cloud status", + "switch": "Switch state", + "total_consumption": "Total consumption", + "current_consumption": "Current consumption", + "over_power": "Over power", + "device_temp": "Device temperature", + "over_temp": "Over temperature", + "battery": "Battery level", + "voltage": "Voltage", + "total_returned": "Total returned", + "current": "Current", + "power_factor": "Power factor", + "click_type": "Click type", + "click_count" :"Click count", + "tilt" :"Tilt", + "vibration" : "Vibration", + "temperature" :"Temperature", + "humidity" : "Humidity", + "illuminance" : "Illuminance", + "ppm": "PPM", + "sensor": "Sensor state", + "total_work_time": "Total work time", + "latest_beta_fw_version": "Latest beta version", + "mqtt": "MQTT connected", + "cloud": "Cloud status" + }, + "config" : { + "groups" : { + "name": "Friendly Name", + "general" : "Gereral", + "mqtt-integrated": "Integrerad MQTT server", + "mqtt-broker": "MQTT broker connection", + "discovery": "Discovery", + "cloud": "Shelly Cloud" + }, + "additional_information": { + "title": "Additional Information", + "desc": "Poll Shelly devices for additional information, like device temperature, warnings, RSSI, uptime, firmware etc." + }, + "mdns": { + "title": "mDns", + "desc": "Enable mDns for discovery, try this if your Shelly devices not discovered" + }, + "version": { + "title": "Show version", + "desc": "Create a entry showing current version of ShellyForHass" + }, + "upgrade_switch": { + "title": "Show upgrade switch", + "desc": "Create upgrade switch when Shelly has new firmware" + }, + "upgrade_beta_switch": { + "title": "Show upgrad to beta switch", + "desc": "Enable upgrade to Beta version switch" + }, + "igmp_fix": { + "title": "IGMP fix", + "desc": "Repeatly send IGMP ADD_MEMBER, try this if instant update stop some minutes" + }, + "host_ip": { + "title": "Host IP to bind to", + "desc": "Ip address for Home Assistant, set to HA to get same IP as Home Assistant" + }, + "mqtt_port": { + "title": "MQTT Port (Integrated MQTT server)", + "desc": "Tcpip port of integrated MQTT server" + }, + "scan_interval": { + "title": "Poll interval (sec)", + "desc": "Interval to poll Shelly for additional information" + }, + "cloud_auth_key": { + "title": "Shelly Cloud Athentication Key", + "desc": "Shelly Cloud Key, used to connect to Shelly Cloud" + }, + "cloud_server": { + "title": "Shelly Cloud Server Name", + "desc": "Shelly Cloud Server Host, used to connect to Shelly Cloud" + }, + "tmpl_name": { + "title": "Friendly Name Template", + "desc": "Template for friendly name received from Shelly Cloud" + }, + "username": { + "title": "Global User Name", + "desc": "User name, used to login to all Shelly devices with restricted login" + }, + "password": { + "title": "Global Password", + "desc": "Password, used to login to Shelly devices with restricted login" + }, + "unavailable_after_sec": { + "title": "Unavailable time", + "desc": "Number of seconds without connection before set device to unavailable" + }, + "mqtt_server_host": { + "title": "MQTT broker Host or IP-address", + "desc": "Address to the MQTT broker to connect to" + }, + "mqtt_server_port": { + "title": "MQTT broker port", + "desc": "Port to the MQTT broker " + }, + "mqtt_server_username": { + "title": "MQTT broker user name", + "desc": "User name to use to login to the MQTT broker" + }, + "mqtt_server_password": { + "title": "MQTT Password", + "desc": "Password to use to login to the MQTT broker" + }, + "discovery": { + "title": "Discovery", + "desc": "Enable automatically discovery of devices" + }, + "show_id_in_name": { + "title": "Show Id in name", + "desc": "Add device ID to the end of friendly name" + } + } + }, "config": { "abort": { "single_instance_allowed": "Only a single configuration of Shelly is allowed." diff --git a/custom_components/shelly/ws_handler.py b/custom_components/shelly/ws_handler.py index 180c62e..2156451 100644 --- a/custom_components/shelly/ws_handler.py +++ b/custom_components/shelly/ws_handler.py @@ -9,6 +9,7 @@ CONF_UNIT, ALL_ATTRIBUTES, ALL_SENSORS, DEFAULT_SETTINGS, DOMAIN, CONF_DECIMALS, CONF_DIV, CONF_SETTINGS, ALL_CONFIG, GLOBAL_CONFIG, DEBUG_CONFIG, DEVICE_CONFIG) import json +from homeassistant.helpers.translation import async_get_translations async def setup_ws(instance): """Set up WS API handlers.""" @@ -18,8 +19,14 @@ async def setup_ws(instance): websocket_api.async_register_command(hass, shelly_setting) @websocket_api.async_response -@websocket_api.websocket_command({vol.Required("type"): "s4h/get_config"}) +@websocket_api.websocket_command({vol.Required("type"): "s4h/get_config", vol.Required("language"): cv.string}) async def shelly_get_config(hass, connection, msg): + resources = await async_get_translations( + hass, + msg["language"], + 'frontend', + 'shelly' + ) #print("GET CONFIG*****************") """Handle get config command.""" content = {} @@ -43,11 +50,12 @@ async def shelly_get_config(hass, connection, msg): unit = conf_setting.get(CONF_UNIT, "") div = conf_setting.get(CONF_DIV, "") decimals = conf_setting.get(CONF_DECIMALS, "") - image = cfgSensor[sensor][2] if sensor else "" default = DEFAULT_SETTINGS.get(id, {}) + base = "component.shelly.frontend.settings." + id + title = resources.get(base, id) + settings.append({'id': id, - 'name': id, - 'image': image, + 'title': title, 'has' : { 'sensor' : id in ALL_SENSORS, 'attrib' : id in ALL_ATTRIBUTES, @@ -68,7 +76,7 @@ async def shelly_get_config(hass, connection, msg): 'unit': default.get(CONF_UNIT) } }) - settings.sort(key=lambda x: x.get('name')) + settings.sort(key=lambda x: x.get('title')) options["settings"] = settings configs = [] config_list = GLOBAL_CONFIG @@ -76,10 +84,17 @@ async def shelly_get_config(hass, connection, msg): config_list = GLOBAL_CONFIG + DEBUG_CONFIG for key in config_list: value = ALL_CONFIG[key] + base = "component.shelly.frontend.config." + key + "." + name = resources.get(base + "title", key) + desc = resources.get(base + "desc") + group = value.get("group") + group = resources.get("component.shelly.frontend.config.groups." + group, group) if "type" in value: configs.append( { "id" : key, - "name" : key, + "title" : name, + "desc" : desc, + "group": group, "type" : value["type"], "value" : instance.conf.get(key, ''), "default" : value.get('def', '')