diff --git a/.github/workflows/upload-driver-packages.yml b/.github/workflows/upload-driver-packages.yml index dc02084047..be2c32359d 100644 --- a/.github/workflows/upload-driver-packages.yml +++ b/.github/workflows/upload-driver-packages.yml @@ -37,7 +37,7 @@ jobs: - name: Package the driver working-directory: ${{ matrix.driver }} run: | - zip -v ${{env.package_key}}.zip config.yml fingerprints.yml search-parameters.y*ml $(find profiles -name "*.y*ml") $(find . -name "*.lua") -x "*test*" + zip -v ${{env.package_key}}.zip config.yml fingerprints.yml search-parameters.y*ml $(find . -name "*.crt") $(find profiles -name "*.y*ml") $(find . -name "*.lua") -x "*test*" - name: Upload packaged driver artifact uses: actions/upload-artifact@v3 with: diff --git a/drivers/SmartThings/matter-switch/src/init.lua b/drivers/SmartThings/matter-switch/src/init.lua index edd6af7c63..dca3820d04 100644 --- a/drivers/SmartThings/matter-switch/src/init.lua +++ b/drivers/SmartThings/matter-switch/src/init.lua @@ -23,6 +23,11 @@ local RECEIVED_X = "receivedX" local RECEIVED_Y = "receivedY" local HUESAT_SUPPORT = "huesatSupport" local CONVERSION_CONSTANT = 1000000 +-- These values are taken from the min/max definined in the colorTemperature capability +local COLOR_TEMPERATURE_KELVIN_MAX = 30000 +local COLOR_TEMPERATURE_KELVIN_MIN = 1 +local COLOR_TEMPERATURE_MIRED_MAX = CONVERSION_CONSTANT/COLOR_TEMPERATURE_KELVIN_MIN +local COLOR_TEMPERATURE_MIRED_MIN = CONVERSION_CONSTANT/COLOR_TEMPERATURE_KELVIN_MAX local function convert_huesat_st_to_matter(val) return math.floor((val * 0xFE) / 100.0 + 0.5) @@ -217,6 +222,10 @@ end local function temp_attr_handler(driver, device, ib, response) if ib.data.value ~= nil then + if (ib.data.value < COLOR_TEMPERATURE_MIRED_MIN or ib.data.value > COLOR_TEMPERATURE_MIRED_MAX) then + device.log.warn_with({hub_logs = true}, "Device reported color temperature %d mired outside of supported capability range", ib.data.value) + return + end local temp = utils.round(CONVERSION_CONSTANT/ib.data.value) local most_recent_temp = device:get_field(MOST_RECENT_TEMP) -- this is to avoid rounding errors from the round-trip conversion of Kelvin to mireds diff --git a/drivers/SmartThings/matter-switch/src/test/test_matter_switch.lua b/drivers/SmartThings/matter-switch/src/test/test_matter_switch.lua index 25b21d5c33..4063e8e74b 100644 --- a/drivers/SmartThings/matter-switch/src/test/test_matter_switch.lua +++ b/drivers/SmartThings/matter-switch/src/test/test_matter_switch.lua @@ -529,4 +529,18 @@ test.register_message_test( } ) +test.register_message_test( + "Do not report when receiving a color temperature of 0 mireds", + { + { + channel = "matter", + direction = "receive", + message = { + mock_device.id, + clusters.ColorControl.attributes.ColorTemperatureMireds:build_test_report_data(mock_device, 1, 0) + } + } + } +) + test.run_registered_tests() diff --git a/drivers/SmartThings/matter-window-covering/src/init.lua b/drivers/SmartThings/matter-window-covering/src/init.lua index c48e7c14ba..a553a5e06e 100644 --- a/drivers/SmartThings/matter-window-covering/src/init.lua +++ b/drivers/SmartThings/matter-window-covering/src/init.lua @@ -97,7 +97,8 @@ local function current_status_handler(driver, device, ib, response) ) or DEFAULT_LEVEL for _, rb in ipairs(response.info_blocks) do if rb.info_block.attribute_id == clusters.WindowCovering.attributes.CurrentPositionLiftPercent100ths.ID and - rb.info_block.cluster_id == clusters.WindowCovering.ID then + rb.info_block.cluster_id == clusters.WindowCovering.ID and + rb.info_block.data.value ~= nil then position = 100 - math.floor((rb.info_block.data.value / 100)) end end diff --git a/drivers/SmartThings/philips-hue/src/init.lua b/drivers/SmartThings/philips-hue/src/init.lua index 9e3af1d99a..315f82d826 100644 --- a/drivers/SmartThings/philips-hue/src/init.lua +++ b/drivers/SmartThings/philips-hue/src/init.lua @@ -138,7 +138,7 @@ local function emit_light_status_events(light_device, light) ) ) else - light_device:emit_event(capabilities.colorControl.hue(adjusted_sat)) + light_device:emit_event(capabilities.colorControl.saturation(adjusted_sat)) end end end diff --git a/drivers/SmartThings/sonos/src/api/sonos_connection.lua b/drivers/SmartThings/sonos/src/api/sonos_connection.lua index 27d854938d..9db887d4ea 100644 --- a/drivers/SmartThings/sonos/src/api/sonos_connection.lua +++ b/drivers/SmartThings/sonos/src/api/sonos_connection.lua @@ -351,7 +351,7 @@ end function SonosConnection:is_running() local self_running = self:self_running() local coord_running = self:coordinator_running() - log.debug(string.format("%s all connections running? %s", self.device.label, {coordinator = self_running, mine = self_running})) + log.debug(string.format("%s all connections running? %s", self.device.label, st_utils.stringify_table({coordinator = self_running, mine = self_running}))) return self_running and coord_running end diff --git a/drivers/SmartThings/sonos/src/init.lua b/drivers/SmartThings/sonos/src/init.lua index ae4c523aec..fbcab354f5 100644 --- a/drivers/SmartThings/sonos/src/init.lua +++ b/drivers/SmartThings/sonos/src/init.lua @@ -84,10 +84,17 @@ local function find_player_for_device(driver, device, should_continue) end local function update_fields_from_ssdp_scan(driver, device, fields) - local current_player_id = device:get_field(PlayerFields.PLAYER_ID) - local current_url = device:get_field(PlayerFields.WSS_URL) - local current_household_id = device:get_field(PlayerFields.HOUSEHOULD_ID) - local sonos_conn = device:get_field(PlayerFields.CONNECTION) --- @type SonosConnection|nil + device.log.debug("Updating fields from SSDP scan") + local is_initialized = device:get_field(PlayerFields._IS_INIT) + + local current_player_id, current_url, current_household_id, sonos_conn + + if is_initialized then + current_player_id =device:get_field(PlayerFields.PLAYER_ID) + current_url = device:get_field(PlayerFields.WSS_URL) + current_household_id = device:get_field(PlayerFields.HOUSEHOULD_ID) + sonos_conn = device:get_field(PlayerFields.CONNECTION) + end local already_connected = sonos_conn ~= nil local refresh = false @@ -170,6 +177,7 @@ local function _initialize_device(driver, device) driver.datastore.dni_to_device_id[device.device_network_id] = device.id end if not device:get_field(PlayerFields._IS_SCANNING) then + device.log.debug("Starting Scan in _initialize_device for %s", device.label) device:set_field(PlayerFields._IS_SCANNING, true) cosock.spawn( function() @@ -181,7 +189,7 @@ local function _initialize_device(driver, device) driver._field_cache[device.device_network_id] if not is_already_found then - log.debug("Rescanning for player with DNI " .. device.device_network_id) + device.log.debug(string.format("Rescanning for player with DNI %s", device.device_network_id)) device:offline() local success = false @@ -198,7 +206,7 @@ local function _initialize_device(driver, device) end end - log.trace("Setting persistent fields") + device.log.trace("Setting persistent fields") local fields = driver._field_cache[device.device_network_id] update_fields_from_ssdp_scan(driver, device, fields) diff --git a/drivers/SmartThings/zwave-valve/src/inverse_valve/init.lua b/drivers/SmartThings/zwave-valve/src/inverse_valve/init.lua index e610d265fe..510e79019b 100644 --- a/drivers/SmartThings/zwave-valve/src/inverse_valve/init.lua +++ b/drivers/SmartThings/zwave-valve/src/inverse_valve/init.lua @@ -17,6 +17,8 @@ local capabilities = require "st.capabilities" local cc = require "st.zwave.CommandClass" --- @type st.zwave.CommandClass.SwitchBinary local SwitchBinary = (require "st.zwave.CommandClass.SwitchBinary")({ version=2, strict=true }) +--- @type st.zwave.CommandClass.Basic +local Basic = (require "st.zwave.CommandClass.Basic")({ version=1, strict=true }) local valve_defaults = require "st.zwave.defaults.valve" local function open_handler(driver, device, command) @@ -42,6 +44,9 @@ local inverse_valve = { zwave_handlers = { [cc.SWITCH_BINARY] = { [SwitchBinary.REPORT] = binary_report_handler + }, + [cc.BASIC] = { + [Basic.REPORT] = binary_report_handler } }, capability_handlers = { diff --git a/drivers/SmartThings/zwave-valve/src/test/test_inverse.valve.lua b/drivers/SmartThings/zwave-valve/src/test/test_inverse.valve.lua index eb2a5bb96a..db7d6be656 100644 --- a/drivers/SmartThings/zwave-valve/src/test/test_inverse.valve.lua +++ b/drivers/SmartThings/zwave-valve/src/test/test_inverse.valve.lua @@ -18,6 +18,7 @@ local zw = require "st.zwave" local zw_test_utils = require "integration_test.zwave_test_utils" local t_utils = require "integration_test.utils" local SwitchBinary = (require "st.zwave.CommandClass.SwitchBinary")({version=2}) +local Basic = (require "st.zwave.CommandClass.Basic")({ version=1 }) local fortrezz_valve_endpoints = { { @@ -77,6 +78,42 @@ test.register_message_test( } ) +test.register_message_test( + "Basic valve on/off report should be handled: off", + { + { + channel = "zwave", + direction = "receive", + message = { mock_device.id, zw_test_utils.zwave_test_build_receive_command( + Basic:Report({value=SwitchBinary.value.OFF_DISABLE}) + ) } + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("main", capabilities.valve.valve.open()) + } + } +) + +test.register_message_test( + "Basic valve on/off report should be handled: on", + { + { + channel = "zwave", + direction = "receive", + message = { mock_device.id, zw_test_utils.zwave_test_build_receive_command( + Basic:Report({value=SwitchBinary.value.ON_ENABLE}) + ) } + }, + { + channel = "capability", + direction = "send", + message = mock_device:generate_test_message("main", capabilities.valve.valve.closed()) + } + } +) + test.register_coroutine_test( "Turning valve on should generate correct zwave messages", function() diff --git a/tools/deploy.py b/tools/deploy.py index 5643025a81..1011eacd7c 100644 --- a/tools/deploy.py +++ b/tools/deploy.py @@ -1,4 +1,4 @@ -import os, subprocess, requests, json, time, yaml +import os, subprocess, requests, json, time, yaml, csv BRANCH = os.environ.get('BRANCH') ENVIRONMENT = os.environ.get('ENVIRONMENT') @@ -31,6 +31,23 @@ drivers_updated = [] uploaded_drivers = {} +## do translations here +LOCALE = os.environ.get('LOCALE') +if LOCALE: + LOCALE = LOCALE.lower() + + current_path = os.path.dirname(__file__) + localization_dir = os.path.join(current_path, "localizations") + localization_file = os.path.join(localization_dir, LOCALE+".csv") + slash_escape = str.maketrans({"/": r"\/"}) + if os.path.isfile(localization_file): + print("Localizing from english to "+LOCALE+" using "+str(localization_file)) + with open(localization_file) as csvfile: + reader = csv.reader(csvfile) + for row in reader: + print("en: "+row[0]+" "+LOCALE+": "+row[1]) + subprocess.run("find . -name 'fingerprints.yml' | xargs sed -i '' 's/deviceLabel: "+row[0].translate(slash_escape)+"/deviceLabel: "+row[1].translate(slash_escape)+"/g'", shell=True) + # Get drivers currently on the channel response = requests.get( ENVIRONMENT_URL+"/channels/"+CHANNEL_ID+"/drivers", diff --git a/tools/localizations/cn.csv b/tools/localizations/cn.csv new file mode 100644 index 0000000000..5d1861ac3a --- /dev/null +++ b/tools/localizations/cn.csv @@ -0,0 +1,38 @@ +Orvibo Gas Detector,欧瑞博 可燃气体报警器 +GDKES Switch 1,粤奇胜智能墙面开关 1 +Button,无线开关 +DG Light,DG智能灯 +GDKES Outlet,粤奇胜智能插座 +GDKES Switch 1,粤奇胜智能墙面开关 1 +HEIMAN Button,海曼智能紧急按钮 +HEIMAN Carbon Monoxide Sensor,海曼一氧化碳报警器 +HEIMAN Gas Detector,海曼燃气报警器 +HEIMAN Motion Sensor,海曼人体红外传感器 +HEIMAN Multipurpose Sensor,海曼温湿度传感器 +HEIMAN Open/Closed Sensor,海曼门窗传感器 +HEIMAN Remote Control,海曼情景开关 +HEIMAN Scene Panel,海曼情景开关 +HEIMAN Siren,海曼智能声光报警器 +HEIMAN Smoke Detector,海曼烟雾报警器 +HEIMAN Switch 1,海曼智能墙面开关 1 +HEIMAN Water Leak Sensor,海曼水浸探测器 +HONYAR Outlet,鸿雁智能插座 +HONYAR Switch 1,鸿雁智能墙面开关 1 +Light,智能球泡灯 +Motion Sensor,人体传感器 +Multipurpose Sensor,温湿度传感器 +Orvibo Outlet,欧瑞博智能插座 +Orvibo Smoke Detector,欧瑞博 烟雾报警器 +Orvibo Switch 1,欧瑞博智能墙面开关 1 +Samsung Door Lock,SDS联网型智能锁 +Window Treatment,智能窗帘电机 +Wistar Window Treatment,威仕达开合帘电机 +Zigbee Dimmer,Zigbee调光器 +Zigbee Light,Zigbee灯 +Zigbee Lock,Zigbee锁 +Zigbee Meter,Zigbee功率计 +Zigbee Switch,Zigbee开关 +Zigbee Thermostat,Zigbee温控 +Zigbee Thing,Zigbee设备 +Zigbee Vent,Zigbee新风系统 +Zigbee Window Treatment,Zigbee窗帘电机 \ No newline at end of file